Подтвердить что ты не робот

Что мне нужно сделать, прежде чем удалять элементы в векторе указателей на динамически выделенные объекты?

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

  • Правда ли, что когда я закончил с вектором, я должен пропустить его и вызвать delete для каждого указателя?
  • Почему мне не нужно называть delete на векторе или любой другой переменной, которую я объявляю без нового оператора, но delete нужно вызывать на указателях?
  • Помогает ли С++ освободить память указателей для меня, если вектор объявлен в функции, которая возвращает (вызывая отклонение вектора от области видимости)?
4b9b3361

Ответ 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;