2009-02-15 1 views
32

В моих первоначальных базовых тестах это совершенно безопасно. Тем не менее, мне показалось, что попытка манипулировать this позже в функции, которая delete s this может быть ошибкой во время выполнения. Это правда, и нормально ли оно безопасно delete this? или существуют ли только определенные случаи, когда это безопасно?Безопасно ли «удалить это»?

+1

Я не согласен. Два вопроса связаны с вопросом о конкретном случае, и когда вы будете использовать это удаление. Я спрашиваю, безопасен ли он в общем смысле, а когда нет. –

ответ

44

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

Он обычно находится в справочниках подсчета классов, которые, когда реф-счетчик уменьшенные до 0,/что функция члена DecrementRefCount()/Release() вызывает delete this.

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

Кроме того, delete this - это «запах кода», который может быть несовместим с тем, что ваш код может не иметь симметричной стратегии для владения объектами (кто выделяет и удаляет). Объект не мог выделить new, поэтому вызов delete this означает, что класс A выделяет объект, но класс B позже освобождает его [self].

+1

Технически, ваш последний абзац неверен. Хотя это правда, что объект не мог выделить себя, это не означает, что вы идете дальше. Поэтому не следует, что другой класс выделил его. Статический заводский метод в том же классе мог бы сделать создание. –

+3

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

+7

Вы не упомянули [вы не можете называть 'delete this' в деструкторе] (http://stackoverflow.com/a/447531/111307). – bobobobo

3

Да. Это должно быть прекрасно. «Это» - всего лишь указатель. Любой указатель сделает для удаления. Информация о том, как удалить объект, содержится в записях кучи. Вот как IUnknown :: Release() обычно реализуется в COM-объектах.

15

Безопасно удалять «это», если это по существу последняя операция в методе. На самом деле это делают несколько API-интерфейсов профессионального уровня (см. Пример реализации CComObject ATL для примера).

Единственная опасность заключается в попытке получить доступ к любым другим данным элемента после вызова «удалить это». Это, безусловно, небезопасно.

+4

Существует также опасность вызвать 'delete this' в деструкторе – bobobobo

2

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

3

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

Удалить это очень удобно, когда вы реализуете ссылочный подсчитанный объект, примером которого являются классы COM.

+0

Что вы имеете в виду, когда говорите «вы в основном потеряли все объекты под этим конкретным классом? Можете ли вы это объяснить? Может быть, пример? –

+4

Ваш пример верен только в том случае, если вы не используете виртуальный деструктор. вы совершенно правы, но опять-таки гирархия с виртуальными деструкторами - это чистое зло, поэтому «никогда» не увидит, что это произойдет. –

12

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

+1

Отличная точка. «delete this» должно действительно выполняться, если вы точно знаете, как был создан объект Это обычно означает наличие частного конструктора. –

+0

В моем случае он находится внутри конечного автомата, поэтому, когда текущее состояние завершено, оно создает новое состояние и заменяет единственный указатель на него в обработчике состояния, а затем ' удалить его. –

+8

«Правильное» решение состоит в том, чтобы иметь частный деструктор. Затем объект может вызвать удаление этого, но вы не можете установить версию на основе стека. – hacken

6

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

Один из способов сделать это - сделать как конструктор, так и деструктор закрытым и обеспечить создание объекта с помощью фабричной функции класса, которая создает объект в куче и возвращает указатель на него. Фабрика классов может быть статической функцией-членом или функцией друга. Затем очистка может быть выполнена с помощью метода Delete() для объекта, который выполняет «delete this». COM-объекты в основном работают таким образом, за исключением того, что в дополнение к ним они подсчитываются с помощью «delete this», когда счетчик ссылок уменьшается до нуля.

13

но не делайте это в деструкторе!

+1

... или в конструкторе :) – Constantin

+2

Да, это было бы плохо. –

+1

в конструкторе вы можете это сделать, если у класса нет нетривиального деструктора – 4pie0

2

Юридический Да
Сейф Нет

-2

Если вы наследовать от базового класса и дал удалить это в функции базового класса, используя полученный указатель класса приведет к аварии. Например:

class Base 
{ 

    virtual void Release() 
    { 
     delete this; 
    } 

} 

class Derived : public Base 
{ 

    void Foo() 
    { 
     ... 
    } 

} 

main() 
{ 

    Base *ptrDerived = new Derived(); 
    ptrDerived->release(); 
    ptrDerived->Foo() //Crash 

} 
+0

Что вы ожидали? Любое использование удаленного объекта - UB, следовательно, краш или хуже. Это не относится к 'delete this' вообще. Вы как-то ожидали, что часть 'Derived' объекта останется в живых? Тогда вы ничего не знаете о жизни объекта C++. Кроме того, что это: 'Release()' или 'release()'? –

0

Считается плохая практика использовать delete this.

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

1. Оператор delete работает только для объектов, размещённых с помощью new оператора. Если объект создан с использованием new, мы можем сделать delete this, иначе поведение не определено.

class A { 
    public: 
    void fun(){ 
    delete this; 
    } 
    }; 

int main(){ 
    /* Following is valid */ 
    A *ptr = new A; 
    ptr->fun(); 
    ptr = NULL // make ptr NULL to make sure that things are not accessed using ptr. 


    /* And following is invalid: Undefined Behavior */ 
    A a; 
    a.fun(); 

    return 0; 
} 

2. После того, как delete this сделано, любой член удаленный объект не должен быть доступен после удаления.

class A { 
    int x; 
    public: 
    A() { 
     x = 0; 
    } 
    void fun() { 
     delete this; 

    /* Invalid: Undefined Behavior */ 
    cout<<x; 
    } 
}; 

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

  • Нет связанных вопросов^_^