Что мне нужно сделать перед удалением элементов в векторе указателей на динамически размещаемые объекты? - PullRequest
19 голосов
/ 31 октября 2010

У меня есть вектор, который я заполняю указателями на объекты.Я пытаюсь научиться хорошему управлению памятью, и у меня есть несколько общих вопросов:

  1. Правда ли, что когда я закончу с вектором, я должен пройти через него и вызвать delete для каждого указателя?
  2. Почему я не должен вызывать delete для вектора или любой другой переменной, которую я объявляю без нового оператора, но delete должен вызываться для указателей?
  3. Обрабатывает ли C ++ освобождение памяти указателейдля меня, если вектор объявлен в функции, которая возвращает (в результате чего вектор выходит из области видимости)?

Ответы [ 5 ]

23 голосов
/ 31 октября 2010
  1. Да
  2. Векторы реализованы с использованием шаблонных распределителей памяти, которые заботятся об управлении памятью, поэтому они являются чем-то особенным.Но, как правило, вам не нужно вызывать delete для переменных, которые не объявлены с ключевым словом new из-за разницы между распределением стека и кучи.Если материал размещен в куче, он должен быть удален (освобожден) для предотвращения утечек памяти.
  3. Нет.Вы явно должны вызывать delete myVec[index] во время итерации по всем элементам.

Пример:

for(int i = 0; i < myVec.size(); ++i)
   delete myVec[i];

С учетом сказанного, если вы планируете хранить указатели в вектореЯ настоятельно рекомендую использовать boost::ptr_vector, который автоматически удалит удаление.

11 голосов
/ 31 октября 2010

Правда ли, что когда я закончу с вектором, я должен пройти через него и вызвать 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>());
}

Если у вас нет компилятора C ++ 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 голосов
/ 31 октября 2010

Все, что вы выделяете с new, вы должны delete позже.Объекты, которые вы явно не выделяете с помощью new, не должны delete.

Если вы не хотите управлять объектами вручную, но хотите, чтобы вектор «владел» ими, это может бытьлучше хранить объекты по значению, а не хранить указатели на них.Таким образом, вместо std::vector<SomeClass*> вы можете использовать std::vector<SomeClass>.

3 голосов
/ 31 октября 2010

Вы также можете использовать std :: unique_ptr, если у вас есть доступ к C ++ 0x. Он заменяет устаревший std :: auto_ptr, который нельзя использовать в контейнерах.

2 голосов
/ 14 сентября 2014

В качестве альтернативы 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;
...