Ist 'this->~T();' möglich?



  • Hi,

    ich habe einen Destruktor simuliert, und die Funktion sieht wie folgt aus:

    void T::destructor() {
        this->~T();
    }
    

    Ich rufe den Destruktor explizit auf, allerdings von der eigenen Memberfunktion.
    Das Programm lauft, die Klasse wird gelöscht... aber wie kann das eigentlich sein?
    Die Funktion loescht die eingene Klasse und kehrt dann zurück, obwohl sie eigentlich nicht mehr existieren dürfte.

    Ich hab hier nachgefragt, weil mir die Situation etwas suspekt vorkommet. Was wäre, wenn ich danach noch etwas zurück geben möcht:

    bool T::destructor() {
        this->~T();
        return true;
    }
    

    Hab ich nicht ausprobiert, aber die erste Funktion scheint zu funktionieren. 😕



  • also schonmal von vornherein: das this-> kannste dir sparn.

    und dann:
    Der Destruktor wird aufgerufen wenn das Objekt zerstört wird; nicht zerstört der Destruktor beim Aufruf das Objekt.

    (steinigt mich wenn ich was falsches erzähle)



  • @destructor()
    Verstehe ich dich richtig? Du willst in deinem Code T->destructor() schreiben, wodurch die Klasse gelöscht werden soll? Der Code

    void T::destructor() {
        this->~T();
    }
    

    löscht deine Klasse nicht. Es ruft lediglich den Destruktor der Klasse auf. Wenn du willst, dass der Aufruf von destructor() die Klasse löscht solltest du die Funktion so implementieren:

    void T::destructor() {
        delete this;
    }
    

    Dadurch wird automatisch der Destruktor aufgerufen. (Hoffentlich war das überhaupt das, was du wolltest). Du darfst nach dieser Funktion allerdings auf keine Membervariablen (Funktionen gehen afaik noch) der Klasse zugreifen. Lokale Variablen kannst du aber dennoch in der Funktion erzeugen (Das heisst return true sollte funktionieren)

    Der Destruktor wird unmittelbar vor der Zerstörung des Objektes aufgerufen



  • solange du nicht nach dem Aufruf versuchst auf irgend welche Member zu zugreifen, ist das glaube ich vom Standard her okay.

    Auch wenn ich den Sinn nicht erkenne.



  • @godlikebot
    Klassen werden in C++ überhaupt nicht gelöscht. Weder durch delete noch durch einen Destruktor. Man kann nur Objekte löschen.

    Du darfst nach dieser Funktion allerdings auf keine Membervariablen (Funktionen gehen afaik noch) der Klasse zugreifen.

    In dem Moment wo der Destruktor eines Objekts betreten wurde, gilt dieses Objekt als zerstört. Du darfst nach Ablauf des Dtors überhaupt keine Member mehr referenzieren. Egal ob nun Membervariablen oder Memberfunktionen.

    Lokale Variablen kannst du aber dennoch in der Funktion erzeugen (Das heisst return true sollte funktionieren)

    Korrekt.



  • godlikebot schrieb:

    @destructor()
    Verstehe ich dich richtig? Du willst in deinem Code T->destructor() schreiben, wodurch die Klasse gelöscht werden soll? Der Code

    void T::destructor() {
        this->~T();
    }
    

    löscht deine Klasse nicht. Es ruft lediglich den Destruktor der Klasse auf. Wenn du willst, dass der Aufruf von destructor() die Klasse löscht solltest du die Funktion so implementieren:

    void T::destructor() {
        delete this;
    }
    

    Dadurch wird automatisch der Destruktor aufgerufen.

    Echt? Das Objekt der Klasse liegt aber nicht im Freispeicher sondern auf dem Stack. Ich hab irgendwo gelesen, dass der Operator delete nicht imstande ist, Objekte auf dem Stack zu loeschen, so das man gezwungen ist (hier in einer besonderen Situation bei mir), den Destruktor explizit aufzurufen:

    T obj;
    obj.doStuff();
    obj.~T(); //loescht obj auf dem stack
    

    PS: delete ruft doch den Destruktor des Objekts auf, so dass die Schreibweise

    this->~T(); //die gleiche Semantik haben sollte wie
    delete this;
    

    😕

    Danke fuer die Hilfe.



  • destructor() schrieb:

    PS: delete ruft doch den Destruktor des Objekts auf, so dass die Schreibweise

    this->~T(); //die gleiche Semantik haben sollte wie
    delete this;
    

    Achja: dieses this liegt aber wiederum im Heap. 🙂

    Sollte this->~T(); also das Objekt loeschen und den Speicher im Heap genauso freigeben wie 'delete this'? Das ist jetzt meine konkrete Frage.



  • das kannst du dir sparen, Objekte auf dem Stack werden automatisch zerstoert ( und auch ihr Dtor aufgerufen), wenn das Programm ihren Gueltigkeitsbereich verlaesst.

    Wenn du z. B. in einer Funktion ein Objekt auf dem Stack erstellst, wird es bei verlassen der Funktion GANZ AUTOMATISCH wieder vom Stack geloescht.

    z. B.

    class Foo
    {
        // ...
    };
    
    void bar();
    
    int main()
    {
        bar();
    }
    
    void bar()
    {
        // in der Funktion bar wird ein Foo-Objekt erstellt
        Foo f;
    
        // ...
    
        // so, hier endet die Funktion bar(), und mit dem Ende von bar
        // wird auch der Dtor von f automatisch aufgerufen und der Speicher,
        // den f auf dem Stack belegt, wieder freigegeben
    }
    

    Es ist NIE (zumindest kann ich mir absolut keinen solchen Fall ausdenken) noetig, den Dtor "von Hand" aufzurufen. Das wird automatisch erledigt, sobald das Objekt "stirbt" (d.h. bei einem Aufruf von delete, wenn das Objekt auf dem Heap angelegt wurde, oder bei Verlassen des Gueltigketisbereiches, wenn es auf dem Stack angelegt wurde)



  • Denke dir fuers erste den Konstruktor und Destruktor als normale Methoden.
    Dann:

    A *a = new A(); // Speicherreservierung auf Heap + Konstruktoraufruf
    delete a; // Destruktoraufruf + Speicher freigeben
    
    {
      A a; // Speicherreservierung auf Stack + Konstruktoraufruf
      //(X)
    } // Destruktoraufruf A:~A() + Stackspeicherfreigabe
    

    Wenn Du nun bei (X) den Destruktor aufrufst, wird er logischerweise insgesamt zweimal aufgerufen, denn der Stack schert sich nicht darum ob das Objekt schon destruiert wurde.



  • @Blue-Tiger:

    Eine Anwendung zum Aufruf des Destruktors waere z.B., wenn man ein Objekt loeschen will und ein neues erstellen will, ohne der Speicherplatz neu allokieren zu muessen.

    {
      A a;
    
      a.~A();
      new (&a) A(); // Ruft Konstruktor auf
    }
    

    (Ich hoffe die Syntax war i.O.; sowas macht man eher selten..)



  • Gunnar schrieb:

    @Blue-Tiger:

    Eine Anwendung zum Aufruf des Destruktors waere z.B., wenn man ein Objekt loeschen will und ein neues erstellen will, ohne der Speicherplatz neu allokieren zu muessen.

    {
      A a;
    
      a.~A();
      new (&a) A(); // Ruft Konstruktor auf
    }
    

    (Ich hoffe die Syntax war i.O.; sowas macht man eher selten..)

    1. Glaub nicht, dass das funktioniert.
    2. Welchen Sinn macht das? In so einem Fall legt man das ganze doch eher auf dem Heap an, oder?



  • Doch, es kann sinnvoll sein:

    T& operator=(const T& rhs)
    {
    this->~T(); // altes Objekt löschen
    new (this) T(rhs); // copy-ctor aufrufen
    return *this;
    }
    

    Vorteil ist, dass die Zuweisung genauso wie Konstruktion funktioniert, wobei sich allerdings arge Probleme ergeben, wenn new fehlschlägt: Dann hat man nämlich ein uninitialisiertes Objekt am Hals, das man nicht mehr los wird... Also im Endeffekt doch nicht so sinnvoll 😉
    Ein anderer Grund ist, dass man den Speicher selber verwalten will, z.B. wie std::vector:

    // Platz für 5 Objekte reservieren, aber vorerst nur 2 Objekte speichern
    T* data = (T*)malloc(sizeof(T) * 5);
    new (data) T;
    new (data + 1) T;
    
    void push_back(const T& obj)
    {
    // Neues Objekt im Array speichern
    new (data + 2) T(obj);
    }
    


  • Der Tipp, ich solle "delete this" benutzen funktioniert nicht (oder es liegt an mir). Ich habe folgende Test-Klasse geschrieben, um mal auszuprobieren:

    class Test {
    public:
    	~Test() { cout<<"wird aufgerufen\n"; }
    	void destructor() { delete this; }
    };
    

    Bekomme ne Debug Assertion Failed-Meldung.

    Wie war das mit delete this gemeint?



  • Hallo,
    den Beitrag von Tag bitte ignorieren. Was er da vorstellt ist ein absolutes Anti-Idiom und sollte niemals in einem ordentlichen C++ Programm auftauchen
    (die vielen Probleme dieses Anti-Idiom werden z.B. in Herb Sutters "Exceptional C++" diskutiert).

    Die "richtige" Variante des op= wäre:

    T& operator=(const T& other)
    {
        T temp(other);
        Swap(temp);
        return *this;
    }
    

    Das drückt Copy-Assignment auch mit Hilfe von Copy-Construction aus, hat aber keines der Probleme der Anti-Idiom-Variante (kein Slicing, keine Exception-Safety-Probleme usw.).

    so das man gezwungen ist (hier in einer besonderen Situation bei mir), den Destruktor explizit aufzurufen:

    Es gibt nur *eine* Situation in der man gezwungen ist einen Dtor explizit aufzurufen: nämlich dann, wenn man ein Objekt per placement new angelegt hat. Eine Sache, die äußerst selten vorkommt.

    Sollte this->~T(); also das Objekt loeschen und den Speicher im Heap genauso freigeben wie 'delete this'?

    Nein. Ersteres ruft nur den Dtor des Objekts auf. Letzteres ruft erst den Dtor des Objekt und dann die zur new passenden operator delete-Funktion auf.



  • Hallo,
    delete this ist nur ok, wenn alle hier genannten Bedingungen erfüllt sind.

    Mich dünkt der ein oder andere hier hat die korrekte Verwendung von C++ bisher nicht verstanden.



  • Ich hab mal ne Klasse geschrieben deren abgeleitete Klassen nur aufm Heap erstellt werden können und jederzeit mit delete this zerstört werden können, falls der Threadersteller sowas machen wollte (er hat ja nicht gesagt, was er wollte).

    Hier die Klasse:

    class Base
    {
    protected:
        virtual inline void del(){}
    private:
        inline ~Base(){}
    public:
        Base(){}
        virtual void destroy(){del(); delete this;}
        friend void* operator new(size_t);
        friend void  operator delete(void*, size_t);
    };
    

    Ich hab noch festgestellt, dass man den Destruktor der abgeleiteten Klassen überhaupt nicht erwähnen sollte, sondern alles in die Funktion del() packen sollte.

    mfg Glamdring



  • Hallo,
    du solltest deinen Dtor dringend virtual machen.



  • Ich habe doch geschrieben, dass der angesprochene operator= nicht gut ist... 🙄

    (die vielen Probleme dieses Anti-Idiom werden z.B. in Herb Sutters "Exceptional C++" diskutiert).

    Rate mal, woher dieses "doch nicht so sinnvoll"e Anti-Beispiel stammt 😉



  • tag schrieb:

    Ich habe doch geschrieben, dass der angesprochene operator= nicht gut ist... 🙄

    Mir war dein Hinweis ehrlich gesagt zu "kleingedruckt" 🙂
    Deshalb wollte ich das noch mal etwas expliziter machen.



  • HumeSikkins schrieb:

    Mir war dein Hinweis ehrlich gesagt zu "kleingedruckt" 🙂

    Na gut, das sehe ich ein 🙂


Anmelden zum Antworten