2009-02-15 14 views
32

In meinen ersten grundlegenden Tests ist es vollkommen sicher, dies zu tun. Es ist mir jedoch aufgefallen, dass der Versuch, this später in einer Funktion zu manipulieren, dass delete s this ein Laufzeitfehler sein könnte. Ist das wahr und ist es normalerweise sicher zu delete this? oder gibt es nur bestimmte Fälle, in denen es sicher ist?Ist es sicher, das zu löschen?

+1

stimme ich nicht zu. Die zwei Fragen verbunden zu fragen nach einem bestimmten Fall und wenn man dies verwenden würde löschen. Ich frage, ob es im allgemeinen Sinn sicher ist und wann nicht. –

Antwort

44

delete this ist legal und tut was Sie erwarten würden: es ruft den Destruktor Ihrer Klasse auf und befreit den zugrunde liegenden Speicher. Nach delete this zurückgibt, Ihr this Zeiger Wert nicht ändern, so dass es jetzt ein dangling Zeiger ist, sollte nicht dereferenziert werden. Dies schließt die implizite Dereferenzierung mit den Membervariablen der Klasse ein.

Es wird in der Regel in Bezug gezählt Klassen gefunden, wenn die Referenzzählung auf 0 verringert wird, die DecrementRefCount()/Release()/was auch immer Memberfunktion aufruft delete this.

delete this wird aus vielen Gründen als sehr schlechte Form angesehen. Es ist leicht, nach delete this versehentlich auf Mitgliedsvariablen zuzugreifen. Der Anrufercode erkennt möglicherweise nicht, dass sich Ihr Objekt selbst zerstört hat.

Auch delete this ist ein "Code-Geruch", dass Ihr Code möglicherweise keine symmetrische Strategie für Objektbesitz hat (wer zuweist und wer löscht). Ein Objekt konnte sich new nicht selbst zugewiesen haben, also bedeutet Aufruf delete this, dass Klasse A ein Objekt zuweist, aber Klasse B gibt es später frei [self].

+1

Technisch ist Ihr letzter Absatz falsch. Obwohl es zutrifft, dass ein Objekt sich nicht selbst zugewiesen haben kann, impliziert dies nicht, wohin Sie als nächstes gehen. Es folgt daher nicht, dass eine andere Klasse es zugewiesen hat. Eine statische Factory-Methode für dieselbe Klasse könnte die Erstellung ausgeführt haben. –

+3

"Löschen Sie dies wird aus vielen Gründen in der Regel als sehr schlechte Form betrachtet." Ich denke, das ist der Grund, warum gut gestaltete ref-gezählte Klassen diese nicht löschen; . Sie verwenden den Griff/Körper Idiom. Meiner Meinung nach ist das Löschen fast immer eine schlechte Idee. (Nun, ich weiß * keinen * Grund - aber ich bin vorsichtig) –

+7

Sie erwähnen nicht [Sie können nicht 'löschen dies 'in einem Destruktor] (http://stackoverflow.com/a/447531/111307). – bobobobo

3

Ja. Es sollte vollkommen in Ordnung sein. "Dies" ist nur ein Zeiger. Jeder Zeiger wird für das Löschen tun. Die Informationen zum Löschen eines Objekts sind in den Heap-Datensätzen enthalten. So wird IUnknown :: Release() normalerweise in COM-Objekten implementiert.

15

Es ist sicher, "this" zu löschen, solange es im Wesentlichen die letzte Operation in der Methode ist. In der Tat tun dies mehrere professionelle APIs (siehe die CComObject-Implementierung von ATL für ein Beispiel).

Die einzige Gefahr besteht darin, nach dem Aufruf von "delete this" auf andere Mitgliederdaten zuzugreifen. Dies ist sicherlich unsicher.

+4

Es besteht auch die Gefahr, 'delete this' in einem Destruktor aufzurufen – bobobobo

2

Read für eine ähnliche Diskussion. Ihr Verständnis ist richtig, dass es funktioniert, benötigt wird und gefährlich sein kann, da Sie danach nicht darauf zugreifen können.

3

löschen Dies kann zu einem Problem führen, wenn Sie Unterklassen des Objekts haben, das Sie löschen. Denken Sie daran, dass die Konstruktion von oben nach unten beginnt und die Löschung von unten beginnt. Wenn Sie also in der Mitte der Hierarchie löschen, haben Sie im Grunde alle Objekte unter dieser bestimmten Klasse verloren.

löschen Dies ist sehr praktisch, wenn Sie ein Objekt mit Referenzzählung implementieren, ein Beispiel dafür sind die COM-Klassen.

+0

Was meinst du damit, wenn du sagst: "Du hast im Grunde alle Objekte unter dieser bestimmten Klasse verloren? Kannst du das erklären? Vielleicht ein Beispiel? –

+4

Dein Beispiel ist nur wahr, wenn du keinen virtuellen Destruktor benutzt Sie sind völlig richtig, aber andererseits ist eine Hierarchie ohne virtuelle Destruktoren pure Bösheit, also sollte man es nie "sehen". –

12

Löschen Sie dies ist völlig legal, wie andere bereits erwähnt haben. Es ist riskant für einen zusätzlichen Grund, der noch nicht erwähnt wurde - Sie gehen davon aus, dass das Objekt auf dem Heap zugeordnet wurde. Dies kann schwierig zu garantieren sein, obwohl im Falle von Referenzzählungsimplementierungen im Allgemeinen kein Problem besteht.

+1

Großartiger Punkt. "delete this" sollte nur dann gemacht werden, wenn Sie sicher wissen, wie das Objekt erstellt wurde Dies bedeutet normalerweise, einen privaten Konstruktor irgendeiner Art zu haben. –

+0

In meinem Fall befindet es sich in einer Zustandsmaschine, wenn also der aktuelle Zustand fertig ist, erzeugt es einen neuen Zustand und ersetzt den einzigen Zeiger darauf im Zustandshandler und dann lösche das selbst. –

+8

Die "richtige" Lösung ist ein privater Destruktor, der das Objekt dann aufrufen kann, um dies zu löschen, aber keine stack-basierte Version. – hacken

6

Wie von anderen angegeben, löschen Sie dies ist ein gültiges Idiom, aber damit es sicher ist, müssen Sie sicherstellen, dass das Objekt nie auf dem Stapel instanziiert wird.

Eine Möglichkeit, dies zu tun, ist sowohl der Konstruktor und der Destruktor privaten und erzwingen Objekterstellung durch eine Klasse Factory-Funktion zu machen, die auf dem Heap den das Objekt erstellt und gibt einen Zeiger auf sie. Die Klassenfabrik kann eine statische Elementfunktion oder eine Freundfunktion sein. Die Bereinigung kann dann über eine Delete() - Methode für das Objekt durchgeführt werden, das "delete this" ausführt. COM-Objekte funktionieren grundsätzlich so, außer dass sie zusätzlich mit dem "delete this" -Ziffer gezählt werden, der auftritt, wenn der Referenzzähler auf Null dekrementiert wird.

13

aber tun Sie es nicht im Destruktor!

+1

... oder im Konstruktor :) – Constantin

+2

Ja, das wäre schlecht. –

+1

im Konstruktor können Sie es tun, es sei denn, Klasse hat nicht-trivialen Destruktor – 4pie0

2

Legal Ja
Sicher Nein

-2

Wenn Sie von einer Basisklasse erben und gab diese in der Funktion der Basisklasse löschen, abgeleitete Klasse Zeiger verwendet, wird zu einem Absturz führen. Z. B:

class Base 
{ 

    virtual void Release() 
    { 
     delete this; 
    } 

} 

class Derived : public Base 
{ 

    void Foo() 
    { 
     ... 
    } 

} 

main() 
{ 

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

} 
+0

Was haben Sie erwartet? Jede Verwendung eines gelöschten Objekts ist UB, daher Absturz oder Schlimmeres. Dies ist nicht spezifisch für das "Löschen". Haben Sie irgendwie erwartet, dass der 'Abgeleitete' Teil des Objekts am Leben bleibt? Dann wissen Sie nichts über die Lebensdauer von C++ - Objekten. Also, was ist das: 'Release()' oder 'release()'? –

0

Es gilt schlechte Praxisdelete this zu verwenden.

jedoch, falls verwendet, müssen folgende Punkte berücksichtigt werden:

1. Der delete Operator nur für Objekte arbeitet new Betreiber zugewiesen werden. Wenn das Objekt mit new erstellt wird, können wir delete this tun, sonst ist das Verhalten undefiniert.

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. Sobald delete this getan wird, wird jedes Mitglied des gelöschten Objekts sollte nicht nach dem Löschen zugegriffen werden.

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

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