2012-01-11 2 views
10

Пожалуйста, обратите внимание на следующий код:Что происходит во время `delete this;` statement?

class foo 
{ 
public: 
    foo(){} 
    ~foo(){} 
    void done() { delete this;} 
private: 
    int x; 
}; 

Что происходит (и это действует?) В следующих двух вариантах:

вариант 1:

void main() 
{ 
    foo* a = new foo(); 
    a->done(); 
    delete a; 
} 

вариант 2:

void main() 
{ 
    foo a; 
    a.done(); 
} 

Будет ли второе заявление delete a; на opt ион 1 вызовет исключение или кучу коррупции?

Будет ли вариант2 вызывать исключение или разложение кучи?

+0

Вы забыли открытые скобки для класса по ошибке или это точно скопированный код? – Neophile

+0

@Nerds: опечатка - исправлена ​​... – NirMH

+0

Интересно. Я бы предположил, что первый приведет к сбою segfault или кучи, а второй сделает все, что удаляет указатель на стек. – cha0site

ответ

19

delete this; разрешено, он удаляет объект.

Оба фрагмента кода имеют неопределенное поведение - в первом случае удаление объекта, который уже был удален, а во втором случае удаление объекта с автоматическим временем хранения.

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

+0

И, надеюсь, это будет первый. – Xeo

+1

Неопределенное поведение, лучший и правильный ответ. +1 –

4

Оба могут вызвать ошибку.

Первый указатель удаляется дважды, со вторым delete вызывает ошибку, тогда как вторая выделяется в стеке и не могут быть удалены неявно (ошибка, вызванная первым вызовом деструктора).

+0

Ты избил меня! – Neophile

+0

Я отредактировал вопрос. Я думаю, что он должен быть выделен в кучу, а затем удален с помощью 'done'. Что происходит в этом случае? Удаление в стеке не имеет смысла и, вероятно, не задано OP. – duedl0r

+0

Ваш ответ верный, но вопрос был в том, что произойдет * sort * ошибки. – cha0site

1

И вызовет ошибку, что вы хотите:

void main() 
{ 
    foo* a = new foo(); 
    a->done(); 
} 

Какой компилятор будет расширяться на что-то, как показано ниже, который я надеюсь, удаляющие марки «это» немного менее запутанным.

void __foo_done(foo* this) 
{ 
    delete this; 
} 

void main() 
{ 
    foo* a = new foo(); 
    __foo_done(a); 
} 

Смотрите также Is it legal (and moral) for a member function to say delete this?

+0

Что делать, если это первый член объекта в классе стандартного макета (или POD), который был выделен простым новым, и который не имеет других деструкторов? (Не то, чтобы это не было _insane_, но, ну ...) – Random832

+0

@ Random832: Я не могу понять ваш вопрос, извините. – ronag

+0

В разделе вопросов, которые вы задаете, приведен список условий, при которых 'delete this' является безопасным. Поскольку первый член класса стандартного макета имеет тот же адрес, что и сам родительский объект, похоже, что теоретически это будет другой случай. – Random832

1

Вызов delete this это плохая идея. Тот, кто звонит new, должен позвонить delete. Следовательно, проблемы, которые были освещены в других ответах.

Также при построении массива объектов вы можете иметь утечку памяти/неопределенное поведение.

+0

Почему вы называете «удалить эту» плохую идею? Я бы сказал, что во многих (большинство?) Приложениях это будет самая распространенная форма 'delete'. –

+0

@JamesKanze Почему? 'delete this' - это не обычные способы выпуска объектов. –

+0

@VJovic Это зависит от приложения.Во многих приложениях наиболее частым (или даже единственным) 'delete' будет 'delete this'. В других случаях 'delete' будет систематически находиться в менеджере транзакций или что-то подобное. И в других ... Это зависит от того, почему вы используете динамическую память. Если это для объектов модели, то наиболее часто встречаются 'delete this' или диспетчер транзакций. Если это для сложных структур данных неизвестного размера, тогда владелец структуры выполнит 'delete', и' delete this' почти никогда не произойдет. –

0

В обоих случаях ваша куча будет повреждена. Конечно, вы не можете использовать в варианте 2 «->», если левое значение (в данном случае «a») не является указателем, правильной формой в опции 2 будет a.done(); Но да, вы должны получить кучу, если попытаетесь удалить 1) то, что уже было удалено, или 2) локальная переменная.

Вызов «удалить это» является технически обоснованным и освобождает память.

EDIT мне было интересно, и на самом деле попытался это:

class foo 
{ 
public: 
    foo(){} 
    ~foo(){} 
    void done() { delete this; } 
    void test() { x = 2; } 
    int getx() { return x; } 
private: 
    int x; 
} ; 


int main(int argc, char* argv[]) 
{ 
    foo *a = new foo(); 
    a->done(); 
    a->test(); 
    int x = a->getx(); 
    printf("%i\n", x); 
    return x; 
} 

Вызов printf будет успешным, и x будет содержать значение 2.

+0

Спасибо - на самом деле использование «->» в опции2 было опечаткой, сделанной внешним редактированием, - я снова обновил вопрос. – NirMH

+0

Итак, 'delete this' освободит память объекта из кучи? – NirMH

+0

@NirMH: использование «->» было написано вами. – duedl0r

0

Я бы очень осторожно относился к контексту, в котором вы освобождаете память. Призывая «удалить это»; попытается освободить память, связанную с структурой класса, эта операция не имеет возможности вывести контекст исходного распределения памяти, т. е. если она выделена из кучи или стека. Мой совет будет заключаться в том, чтобы переработать ваш код, чтобы операция освобождения была вызвана из внешнего контекста. Обратите внимание, что это отличается от освобождения структур, встроенных в класс, в этом случае используйте деструктор.

3

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

Это гарантирует, что только класс может удалить себя.

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

+0

, конечно, он не предотвращает все ошибки, например. используя объект после вызова done() на нем (даже если вызов done() снова вызовет двойное удаление). – CashCow

Смежные вопросы