2009-08-11 5 views
7

Я пишу связанный список, и я хочу, чтобы деструктор структуры (структура узла) просто удалял себя и не имел никаких побочных эффектов. Я хочу, чтобы деструктор моего списка, чтобы итеративно назвать 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; 
} 

Допустимо ли это, особенно в этом контексте?

+0

Я собираюсь быть последним, кто пойдет на закрытие, но я считаю, что этот вопрос был задан несколько раз в прошлом: http://www.google.com/search?q=delete + this + site: stackoverflow.com –

ответ

17

Если вызывается деструктор узла, он уже находится в процессе удаления. Таким образом, удаление не имеет смысла внутри деструктора узла.

Кроме того, это не так:

while (temp->next() != NULL) 
{ 
    delete temp; 
    temp = temp->next(); 
} 

Вместо этого вы должны получить TEMP-> следующая() в переменную темп. В противном случае вы получаете доступ к удаленной памяти.

Так больше похоже на это:

DoublyLinkedList::~DoublyLinkedList 
{ 
    Node *temp = first(); 
    while (temp != NULL) 
    { 
     Node *temp2 = temp->next(); 
     delete temp; 
     temp = temp2; 
    } 
} 
+0

Примечание: Поскольку вы удаляете все узлы вашего деструктора DoublyLinkedList, вы не должны удалять объекты Node в другом месте. –

+0

Мне нужен деструктор узла вообще? Я просто не хочу, чтобы сопутствующая компилятору dtor возилась со своей жизнью. – jkeys

+1

Вы можете либо положить один, либо оставить его пустым, иначе вы не сможете его поместить. –

0

Код выше позвонит Node :: ~ Node() дважды. (В "удалить темп" и в узле :: ~ Node())

Node :: ~ Node() не должен вызывать "удалить это" (или программа зависнет)

пс. в то время как цикл в вашем коде не будет работать. Это приведет к разыменованию неверного указателя. Вы должны сначала скопировать значение temp-> next, а затем уничтожить указатель темпа.

+1

Собственно, 'Node :: ~ Node()' не будет вызываться дважды, но заканчивается переполнением стека (Ha!) Из-за бесконечной рекурсии. – sbi

+0

@sbi Вы правы. Это вызовет переполнение стека. – rein

4

Нет, вы не должны delete this от деструктора. Деструктор вызывается из-за инструкции удаления (или выходит за рамки), и это, скорее всего, приведет к некоторому сбою.

У вас также есть пара проблем в DurlyLinkedList desturctor. Во-первых, вы удаляете temp, а затем получаете temp после его удаления. Во-вторых, код фактически не удаляет последний элемент в связанном списке.

1

удалить это; вызовет деструктор текущего объекта. В этом случае, если вы звоните, удалите это; в деструкторе, деструктор будет называться бесконечно до крушения.

4

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

delete temp; 
temp = temp->next(); 

Если вы хотите рекурсивно удалить структуру, вы хотите что-то вроде этого:

DoublyLinkedList::~DoublyLinkedList 
{ 
    Node *temp = first(); 
    delete temp; 
} 

Node::~Node 
{ 
    if(this->next() != NULL) delete this->next(); 
} 
+2

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

+0

Хорошая точка, спасибо. Я хочу избежать рекурсивной реализации, потому что это может стать неэффективным для больших списков, я считаю. – jkeys

+1

Код OP не обязательно приведет к AV - освобожденная память может быть оставлена ​​доступной процессом или может быть возвращена в операционную систему и станет недоступной - это зависит от менеджера кучи. – sharptooth

0

В общем, деструктор должен просто беспокоиться об удалении (или освобождении, если вы используете C или malloc) любую память, выделенную специально для вашего объекта. Удаление указателя на ваш объект всегда будет управляться операционной системой, и вам не нужно беспокоиться об этой части.

Следует иметь в виду, что при построении вы сначала создаете объект (когда поток управления входит в тело конструктора), ТОГДА объекты внутри; для уничтожения вам нужно сделать это наоборот, потому что если вы сначала удалите внешний объект, у вас не будет доступа к внутренним указателям, чтобы их удалить. Вместо этого вы удаляете внутренние объекты с помощью деструктора, тогда ОС управляет фактическим освобождением памяти, когда поток управления выходит из деструктора.

Кстати, подобное происходит с подклассификацией - если у вас есть класс A и класс B: public A, то, когда вы выполняете новый B(), сначала выполняется конструктор A, а затем конструктор B; при уничтожении, деструктор B выполняет сначала, а затем A. Я довольно уверен, что вам НЕ нужно беспокоиться об этом, хотя ... C++ позаботится об этом для вас. Поэтому не пытайтесь выяснить способ вызова delete на суперклассе.

1

Прежде всего: я действительно очень надеюсь, что это домашняя работа, назначенная вам, чтобы понять двойной список. В противном случае нет причин использовать это вместо std::list. С этим в сторону:

Нет, delete this в dtor всегда ошибочно, поскольку dtor вызывается, когда this находится в состоянии его удаления.

Кроме того, в то время как

delete temp; 
temp = temp->next(); 

кстати мог бы работать, это, конечно, неправильно, так как, когда вы пытаетесь получить доступ temp->next(), temp уже удален, так что вы должны вызвать функцию-член на нем. Это вызывает так называемое «неопределенное поведение». (Вкратце: он может делать то, что вы хотите, но это может быть так же неудачно всегда или спорадически или только когда пятница, 13-е, сталкивается с новой луной. Он также вызывает очень противные носовые демоны на вас.)

Обратите внимание, что вы могли бы решить обе проблемы путем удаления следующего узла в dtor вашего узла:

Node::~Node() 
{ 
    delete next(); 
} 

Таким образом, список ваш dtor становится очень легко, тоже:

DoublyLinkedList::~DoublyLinkedList() 
{ 
    delete first(); 
} 

Для меня, это, кажется, что dtors были изобретены, так что, за исключением того факта, что в настоящее время никто не должен писать свои собственные типы связанных списков, для меня это, кажется, решение C++ к вашей проблеме.

+0

Не домашнее задание, но это учебное упражнение. Я никогда не использовал список. Я поклонник STL, поэтому не беспокойтесь об этом. – jkeys

+0

@ Записан: вздох облегчения с этой целью. ': ^>' – sbi

0

Оба никогда не должны делаться.

Это

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 разумен из других методов для освобождения текущего объекта, но не от деструктора.