Можно ли использовать "удалить это" для удаления текущего объекта?
Я пишу связанный список, и я хочу, чтобы деструктор структуры (структура Node) просто удалил сам себя и не имел побочных эффектов. Я хочу, чтобы деструктор моего списка итеративно вызывал деструктор Node сам по себе (временно сохраняя следующий узел), например так:
//my list class has first and last pointers
//and my nodes each have a pointer to the previous and next
//node
DoublyLinkedList::~DoublyLinkedList
{
Node *temp = first();
while (temp->next() != NULL)
{
delete temp;
temp = temp->next();
}
}
Так что это будет мой деструктор Node:
Node::~Node
{
delete this;
}
Это приемлемо, особенно в этом контексте?
8 ответов
Если вызывается деструктор Node, то он уже находится в процессе удаления. Таким образом, удаление не имеет смысла в вашем деструкторе Node.
Также это неправильно:
while (temp->next() != NULL)
{
delete temp;
temp = temp->next();
}
Вместо этого вы должны получить temp->next() в переменной temp. В противном случае вы получаете доступ к удаленной памяти.
Итак, больше похоже на это:
DoublyLinkedList::~DoublyLinkedList
{
Node *temp = first();
while (temp != NULL)
{
Node *temp2 = temp->next();
delete temp;
temp = temp2;
}
}
В настоящее время ваш код может вызвать нарушение прав доступа, поскольку вторая из следующих строк явно обращается к освобожденной памяти:
delete temp;
temp = temp->next();
Если вы хотите рекурсивно удалить структуру, вы хотите что-то вроде этого:
DoublyLinkedList::~DoublyLinkedList
{
Node *temp = first();
delete temp;
}
Node::~Node
{
if(this->next() != NULL) delete this->next();
}
Нет не должен delete this
от деструктора. Деструктор вызывается из-за оператора удаления (или выходит из области видимости), и это, скорее всего, приведет к некоторому сбою.
У вас также есть пара проблем в деструкторе DoublyLinkedList. Во-первых, вы удаляете temp, а затем получаете доступ к temp после ее удаления. Во-вторых, код фактически не удалит последний элемент в связанном списке.
Прежде всего: я действительно, очень надеюсь, что это домашнее задание, назначенное вам для того, чтобы понять двусвязный список. В противном случае нет причин использовать это вместо std::list
, С этим из пути:
Нет, delete this
в дторе всегда неправильно, так как дтор вызывается, когда this
находится в состоянии удаления.
Кроме того, в то время как
delete temp;
temp = temp->next();
кстати может сработать, это уж точно неправильно, т.к. там, где вы пытаетесь получить доступ temp->next()
, temp
уже удален, поэтому вы должны вызвать функцию-член на нем. Это вызывает так называемое "неопределенное поведение". (Короче говоря: он может делать то, что вы хотите, но он также может терпеть неудачу всегда или время от времени, или только когда пятница, 13-е, сталкивается с новолунием. Это также может вызвать на вас очень неприятных носовых демонов.)
Обратите внимание, что вы можете решить обе проблемы, удалив следующий узел в dtor вашего узла:
Node::~Node()
{
delete next();
}
Таким образом, ваш список dtor тоже становится очень простым:
DoublyLinkedList::~DoublyLinkedList()
{
delete first();
}
Мне кажется, для этого и были придуманы dtors, поэтому, за исключением того факта, что в наше время никто больше не должен писать свои собственные типы связанных списков, мне кажется, что это решение C++ для вашей проблемы.
Удалить это; вызовет деструктор текущего объекта. В этом случае, если вы звоните, удалите это; в деструкторе, то деструктор будет бесконечно вызываться до крушения.
Оба никогда не должны быть сделаны.
это
DoublyLinkedList::~DoublyLinkedList
{
Node *temp = first();
while (temp->next() != NULL)
{
delete temp;
temp = temp->next();
}
}
приведет к неопределенному поведению - у вас нет доступа к памяти, которую вы вернули в кучу. Вместо этого должно быть:
DoublyLinkedList::~DoublyLinkedList
{
Node *temp = first();
while( temp != NULL)
{
Node* next = temp->next();
delete temp;
temp = next;
}
}
призвание delete this
приведет к так называемой двойной свободе, которая также вызовет неопределенное поведение. Деструктор должен вызывать delete только для переменных-указателей, но не для this
, призвание delete this
разумно использовать другие методы для освобождения текущего объекта, но не от деструктора.
Код выше вызовет Node::~Node() дважды. (в "delete temp" и в Node::~Node())
Node::~Node() не должен вызывать "delete this" (иначе ваша программа потерпит крах)
пс. Цикл while в вашем коде не будет работать. Будет разыменован неверный указатель. Сначала вы должны скопировать значение temp->next, а затем уничтожить указатель temp.
В общем, деструктор должен просто беспокоиться об удалении (или освобождении, если вы используете C или malloc) любой памяти, выделенной специально для вашего объекта. Удаление указателя на ваш объект всегда будет управляться ОС, и вам не нужно беспокоиться об этом.
Следует иметь в виду, что при создании вы сначала создаете объект (когда поток управления входит в тело конструктора), а затем объекты внутри; для уничтожения вы должны сделать это в обратном порядке, потому что, если вы сначала удалите внешний объект, у вас не будет доступа к внутренним указателям для их удаления. Вместо этого вы удаляете внутренние объекты, используя деструктор, затем ОС управляет фактическим освобождением памяти, когда поток управления выпадает из деструктора.
Кстати, то же самое происходит с подклассами: если у вас есть класс A и класс B: public A, то когда вы делаете новый B(), сначала выполняется конструктор A, затем конструктор B; При уничтожении сначала выполняется деструктор B, а затем A. Я вполне уверен, что вам не нужно беспокоиться об этом, хотя - C++ позаботится об этом за вас. Поэтому не пытайтесь найти способ вызвать delete в суперклассе.