@calculator212

Как правильно освобождать память?

Мой код
#include <iostream>
template <class T>
class Array
{
private:
    int m_length;
    T *m_data;

public:
    Array()
    {
        m_length = 0;
        m_data = nullptr;
    }

    Array(int length)
    {
        m_data = new T[length];
        m_length = length;
    }

    ~Array()
    {
        delete[] m_data;
    }

    void Erase()
    {
       delete[] m_data;
       m_data = nullptr;
       m_length = 0;
    }


    T& operator[](int index)
    {

        return m_data[index];
    }

    int getLength(){return m_length;}
};

int main()
{
    Array<Array<int>> ArrayArray(1);
//    ArrayArray[0] = *new Array<int>{10};
    ArrayArray[0] = Array<int>{10};
    return 0;
}


Проблема такая, что если выделяю память так "ArrayArray[0] = *new Array{10};", то всё отрабатывает нормально, если без new, то приложение падает с double free detected in tcache 2. Как сделать так, чтобы это корректно работало при такой инициализации " ArrayArray[0] = Array{10};".
  • Вопрос задан
  • 229 просмотров
Решения вопроса 1
@MarkusD Куратор тега C++
все время мелю чепуху :)
Давай рассмотрим, для начала, строку под комментарием.

ArrayArray[0] = *new Array<int>{10};
Что тут происходит.
ArrayArray[0] вернет ссылку на Array<int>.
*new Array<int>{10} выделяет память в куче под Array<int>, вызывает инициализатор Array<int>::Array(int length), после чего делает разыменование получившегося указателя на Array<int>.
После этого для подготовленной ссылки на Array<int> будет выполнен оператор копирования по умолчанию, функциональность которого просто скопирует поля из объекта справа в объект слева от присвоения.
После этого результат new Array<int>{10} становится утекшим, т.к. указатель на него сразу становится потерян и освободить занимаемую им память становится невозможно.
Именно поэтому с этой строчкой у тебя "всё отрабатывает нормально". И нет, всё у тебя не отрабатывает нормально потому что память утекла.

Давай рассмотрим следующую строку.

ArrayArray[0] = Array<int>{10};
Что тут происходит.
ArrayArray[0] вернет ссылку на Array<int>.
Array<int>{10} инициализирует безымянный локальный объект на стеке, используя инициализатор Array<int>::Array(int length).
После этого выполняется уже оператор перемещения по умолчанию, суть которого снова сводится к копированию полей из объекта справа в объект слева. После этого оба объекта ссылаются на одну и ту же выделенную память через поле T *m_data.
Далее безымянный локальный объект уничтожается, освобождая недавно выделенную память. А ArrayArray[0] в этот момент начинает ссылаться на освобожденную память.

Double deletion происходит тогда, когда ArrayArray в конце работы программы пытается удалить уже освобожденную память в ArrayArray[0].

В твоем коде на лицо нарушение инварианта типа.
Что нужно сделать.
Тебе должно уже стать понятно что проблема в твоем коде связана с поведением операторов копирования и перемещения по умолчанию. Но проблема у тебя, на самом деле, значительно шире. Потому что завтра ты ведь точно захочешь еще и инициализацию копией провести или вернуть объект по значению из функции. В этом случае тебя тоже ждут проблемы.
Решением твоих проблем будет соблюдение правила 3/5/0.
Тебе нужно полноценно описать поведение объектов при копировании, перемещении, а так же при инициализации копией и инициализацией через перемещение.
Ответ написан
Пригласить эксперта
Ответы на вопрос 1
wataru
@wataru Куратор тега C++
Разработчик на С++, экс-олимпиадник.
В деструкторе удаляйте, только если память не nullptr или длина не 0.
Ответ написан
Комментировать
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы