Освобождение памяти в C++

154
14 марта 2019, 07:00

Есть класс Cinema, а так же производные от него. В консольном приложении я создаю список этих классов.

auto cinemas = new std::list<Cinema*>();
cinemas->push_back(new ImaxCinema("Victory square 15/6", "Imax 1", 150, 150));
cinemas->push_back(new Cinema3D("Victory square 16/1", "3D-Cinema", "Spectrum", 14));
cinemas->push_back(new DriveInCinema("Victory square 17/89", "Drive-In", 15, 102.3));

Позже, с помощью цикла, я вывожу содержимое.

for (auto iterator = cinemas->begin(); iterator != cinemas->end(); iterator++)
{
    (*iterator)->write();
} 

Затем очищаю список и освобождаю память выделенную под список.

cinemas->clear();
delete cinemas;

Вопрос: вызывается ли delete для каждого элемента в списке? Как это можно проверить?

Answer 1

Нет, память неявно не освобождается. Мало того, после вызова clear() Вы потеряли все имеющиеся указатели и, если они нигде более не сохранены, то Вы уже не сможете освободить память.

Вы можете освободить память в цикле, перед вызовом clear():

for (auto p: *cinemas) {
    delete p;
}
cinemas->clear();
delete cinemas;

Также Вы можете использовать умные указатели:

auto cinemas = std::make_unique<std::list<std::unique_ptr<Cinema>>>();
cinemas->push_back(std::make_unique<ImaxCinema>("Victory square 15/6", "Imax 1", 150, 150));
cinemas->push_back(std::make_unique<Cinema3D>("Victory square 16/1", "3D-Cinema", "Spectrum", 14));
cinemas->push_back(std::make_unique<DriveInCinema>("Victory square 17/89", "Drive-In", 15, 102.3));
cinemas->clear();//"Умные" указатели уничтожат объекты, которыми владеют
cinemas.reset();//Удаление объекта, которым владеет cinemas
//Или совсем ничего не нужно вызывать, объект будет уничтожен при выходе из области видимости
Answer 2

Формально педантично-правильный способ удаления элементов из контейнера "голых" указателей должен выглядеть примерно так

while (!cinemas->empty())
{
  Cinema *cinema = сinemas->back();
  cinemas->pop_back();
  delete cinema;
}
delete cinemas;

Идея заключается в том, чтобы ни в какой момент времени в контейнере не хранились невалидные указатели. Т.е. в первую очередь удаляется элемент из контейнера, и только затем уничтожается объект, на который этот элемент указывал.

Вариант, который начинает с удаления всех указуемых объектов и только потом удаляет элементы списка, является практически приемлемым для std::list<>, но запросто может привести к проблемам с другими типами контейнеров (в частности, с ассоциативными контейнерами).

Answer 3
  1. Удалять вручную.

    for(auto cinema: *cinemas) delete cinema;
    cinemas->clear();
    
  2. Заставить компилятор сделать это за вас:

    auto cinemas = new std::list<std::unique_ptr<Cinema>>();
    cinemas->push_back(std::make_unique<ImaxCinema>("Victory square 15/6", "Imax 1", 150, 150));
    cinemas->push_back(std::make_unique<Cinema3D>("Victory square 16/1", "3D-Cinema", "Spectrum", 14));
    cinemas->push_back(std::make_unique<DriveInCinema>("Victory square 17/89", "Drive-In", 15, 102.3));
    cinemas->clear();
    

PS. А cinemas должен быть указателем?

READ ALSO
Почему объявление using вводит не тот метод базового класса, который указан

Почему объявление using вводит не тот метод базового класса, который указан

И почему нельзя использовать объявление using при виртуальном наследовании?

170
Инициализация двух массивов

Инициализация двух массивов

Почему при выполнении кода оба массива постоянно инициализируются (или, по крайней мере, выводятся на экран) одинаковыми числами? https://wwwonlinegdb

183
Как записать несколько чисел в одном рядке в массив [закрыт]

Как записать несколько чисел в одном рядке в массив [закрыт]

Дана задача, в которой надо ввести числа в массив, при этом, вводить их надо через пробел

160
Ошибка при подключении к БД jdbc

Ошибка при подключении к БД jdbc

Вот возникает ошибка при попытке подключения: commysql

170