Что происходит во время `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; в варианте 1 вызвать повреждение исключения или кучи?

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

Ответ 1

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

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

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

Ответ 2

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

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

Ответ 3

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

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

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

Ответ 4

Оба будут вызывать ошибку, что вы хотите:

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

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

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

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

См. также Является ли законным (и моральным) для функции-члена сказать удалить это?

Ответ 5

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

Кроме того, при построении массива объектов может возникать утечка памяти/ undefined.

Ответ 6

В обоих случаях ваша куча будет повреждена. Конечно, вы не можете использовать в варианте 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.

Ответ 7

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