Рекурсивные деструкторы в C ++ - PullRequest
0 голосов
/ 06 января 2012

Предположим, у меня есть класс, подобный следующему:

#include <vector>

class element
{
public:
   element();
   ~element();

   virtual void my_func();

private:
   std::vector<element*> _elements;
};

Как мне поступить с реализацией деструктора?

Я думал, что-то вроде этого, но я не уверен.Меня беспокоят утечки памяти, поскольку я относительно новичок в C ++.

void element::destroy()
{
   for(int i = 0; i < _elements.size(); ++i)
      _elements.at(i)->destroy();

   if(this != NULL) 
      delete this;
}

element::~element()
{
    destroy();
}

Спасибо.

PS: Вот пример main:

int main()
{
   element* el_1 = new element();
   element* el_2 = new element();
   element* el_3 = new element();

   el_1.add_element(el_2);
   el_2.add_element(el_3);

   return 0;
}

Кроме того, что если я сделаю это (иначе не используйте new):

int main()
{
   element el_1;
   element el_2;
   element el_3;

   el_1.add_element(&el_2);
   el_2.add_element(&el_3);

   return 0;
}

Ответы [ 4 ]

8 голосов
/ 06 января 2012
element::~element()
{
    typedef std::vector<element*>::const_iterator iterator;
    for (iterator it(_elements.begin()); it != _elements.end(); ++it)
        delete *it;
}

delete this в деструкторе всегда неверен: this уже уничтожается!

Кроме того, вам нужно будет объявить конструктор копирования и оператор присваивания копии (либо оставив их неопределенными, либо не копируя свой класс, либо предоставив подходящее определение, которое копирует дерево).

В качестве альтернативы (и предпочтительно) вы должны использовать контейнер умных указателей для _elements. Например.,

std::vector<std::unique_ptr<element>> _elements;

Когда element уничтожен, его контейнер _elements будет автоматически уничтожен. Когда контейнер уничтожен, он уничтожит каждый из его элементов. std::unique_ptr владеет объектом, на который он указывает, и когда std::unique_ptr будет уничтожен, он уничтожит элемент, на который он указывает.

Используя std::vector<std::unique_ptr<element>> здесь, вам не нужно предоставлять свой собственный деструктор, потому что все эти встроенные функции позаботятся о вашей очистке.

Если вы хотите иметь возможность копировать дерево element, вам все равно придется предоставить свой собственный конструктор копирования и оператор назначения копирования, который клонирует дерево. Однако если вам не нужно, чтобы дерево было копируемым, вам не нужно объявлять операции копирования, как вы это делаете, если сами управляете памятью: контейнер std::unique_ptr сам по себе не копируется, поэтому его присутствие в качестве члена переменная будет подавлять неявно сгенерированные операции копирования.

3 голосов
/ 06 января 2012
class element
{
public:
   element();
   ~element();

private:
   std::vector<element*> _elements;
};

В соответствии с правилом Три , в нем отсутствует определение конструктор копирования и оператор присваивания .


element::~element()
{
    destroy();
}

Вызов delete this (что делает destroy()) из деструктора всегда неправильно ,потому что деструктор - это то, что вызывается, когда текущий объект удаляется.


for(int i = 0; i < _elements.size(); ++i)
   _elements.at(i)->destroy();

В то время как это делает работу по вызову delete для объектов, для которых вы вызываете destroy(), такое расположение имеет тот недостаток, что вы не должны уничтожать такие объекты, кроме как путем вызова destroy().В частности, это

element el_1;

будет проблемой, потому что e1_1 будет автоматически уничтожено, когда он выпадет из области видимости, вызывая свой деструктор.Так как это вызывает destroy(), что вызывает delete this для объекта, не выделенного с помощью new, это вызывает Неопределенное поведение .(Если вам повезет, он сразу взорвется вам в лицо.)

Было бы намного лучше удалить delete this из деструктора, и только деструктор удалит объекты в векторе объекта:

for(std::vector<element*>::size_type i = 0; i < _elements.size(); ++i)
  delete _elements[i];

if(this != NULL) 
   delete this;

Проверка указателя на NULL ness: никогда не требуется , поскольку определено deleteничего не делать, когда на него передан указатель NULL.

2 голосов
/ 06 января 2012
  1. delete this не должно быть там, так как объект уже разрушается по определению.

  2. Если вы копируете element, то копируются все указатели внутри его члена vector; затем, когда оригинал выходит из области видимости, их пуанты уничтожаются, а копия element имеет vector висящих указателей. Вам нужен оператор копирования и оператор присваивания.

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

Почему здесь вообще требуется динамическое размещение? Почему бы не хранить element сами объекты?

#include <vector>

class element
{
public:
   void add_element(element const& e) {
      _elements.push_back(e);
   }
private:
   std::vector<element> _elements;
};

Это изменит вашу программу тонко, если ранее у вас было несколько ссылок на один и тот же element, переходящий в другой elements, но вам придется решить для себя, нужен ли вам этот запутанный беспорядок зависимостей.

0 голосов
/ 06 января 2012

Нет, так не работает. Вы никогда не удаляете this. Вы просто не По сути, возьмите свой метод уничтожения, поместите все в ~element(), кроме части delete this. И вместо того, чтобы вызывать метод уничтожения, просто delete _elements[i];.

...