Что мне нужно сделать, прежде чем удалять элементы в векторе указателей на динамически выделенные объекты?
У меня есть вектор, который я заполняю указателями на объекты. Я пытаюсь изучить хорошее управление памятью и задать несколько общих вопросов:
- Правда ли, что когда я закончил с вектором, я должен пропустить его и вызвать delete для каждого указателя?
- Почему мне не нужно называть delete на векторе или любой другой переменной, которую я объявляю без нового оператора, но delete нужно вызывать на указателях?
- Помогает ли С++ освободить память указателей для меня, если вектор объявлен в функции, которая возвращает (вызывая отклонение вектора от области видимости)?
Ответы
Ответ 1
- Да
- Векторы реализуются с использованием распределителей памяти шаблонов, которые заботятся об управлении памятью для вас, поэтому они несколько особенные. Но, как правило, вам не нужно вызывать
delete
для переменных, которые не объявлены с помощью ключевого слова new
из-за разницы между распределением стека и кучи. Если материал размещен в куче, он должен быть удален (освобожден), чтобы предотвратить утечку памяти.
- Нет. Вы явно должны называть
delete myVec[index]
, когда вы перебираете все элементы.
Пример:
for(int i = 0; i < myVec.size(); ++i)
delete myVec[i];
С учетом сказанного, если вы планируете хранить указатели в векторе, я настоятельно рекомендую использовать boost::ptr_vector
, который автоматически заботится о удаление.
Ответ 2
Правда ли, что когда я закончил с вектором, я должен пропустить его и вызвать delete на каждом указателе?
Ну, вам не нужно зацикливаться вручную, вы также можете использовать алгоритм:
#include <vector>
#include <algorithm>
#include <memory>
int main()
{
std::vector<Base*> vec;
vec.push_back(new Derived());
vec.push_back(new Derived());
vec.push_back(new Derived());
// ...
std::for_each(vec.begin(), vec.end(), std::default_delete<Base>());
}
Если у вас нет компилятора С++ 0x, вы можете использовать boost:
#include <boost/lambda/lambda.hpp>
#include <boost/lambda/construct.hpp>
std::for_each(vec.begin(), vec.end(), boost::lambda::delete_ptr());
Или вы можете написать свой собственный функтор:
struct delete_ptr
{
template <class T>
void operator()(T* p)
{
delete p;
}
};
std::for_each(vec.begin(), vec.end(), delete_ptr());
Ответ 3
Вы также можете использовать std:: unique_ptr, если у вас есть доступ к С++ 0x. Он заменяет устаревший std:: auto_ptr, который нельзя использовать в контейнерах.
Ответ 4
Все, что вы выделили с помощью new
, вам нужно delete
позже. Объекты, которые вы явно не выделяете с помощью new
, не должны вы delete
.
Если вы не хотите управлять объектами вручную, но хотите, чтобы вектор "владел" ими, лучше было бы хранить объекты по значению, а не хранить указатели на них. Поэтому вместо std::vector<SomeClass*>
вы можете использовать std::vector<SomeClass>
.
Ответ 5
В качестве альтернативы boost::ptr_vector
, как упоминал Дэвид Титаренко, вы можете легко изменить std::vector, чтобы автоматически освободить память для размещения указателей на удаление:
template<class T>
class Group : public std::vector<T>
{
public:
virtual ~Group() {};
};
template<class T>
class Group<T *> : public std::vector<T *>
{
public:
virtual ~Group()
{
std::vector<T *>::reverse_iterator it;
for (it = this->rbegin(); it != this->rend(); ++it)
delete *it;
}
};
Все функции, предоставляемые std::vector, наследуются, поэтому вы добавляете элементы одинаково:
Group<Foo *> *bar = new Group<Foo *>();
bar->push_back(new Foo());
bar->push_back(new DerivedFoo());
// Deleting the Group will free all memory allocated by contained pointers
delete bar;