Kann sich ein Objekt selbst zerstören?



  • Hallo, ist es möglich, dass sich ein Objekt selbst zerstört?
    Beispielsweise so:

    class myclass
    {
    public:
    myclass(){};
    ~myclass(){};
    void destroy(){delete this};
    };
    
    int main()
    {
    myclass mc;
    mc.destroy();
    return 0;
    }
    

    Wenn ich das mache bricht er mit Fehlermeldung ab. Ist es möglich sowas zu machen ohne sich delete zu überladen?
    Also mit normalen delete das auch den Destruktor aufruft?
    Bzw. kann man den Destruktor irgendwie selbst aufrufen?



  • Ein delete auf eine Stack-Adresse müsste undefiniert sein. Bei auf dem Heap erzeugten Objekten sieht das anders aus, aber da ist es trotzdem nicht sehr schön.


  • Mod

    uekue_ schrieb:

    Bzw. kann man den Destruktor irgendwie selbst aufrufen?

    Ja
    this->~myclass()
    Allerdings musst du dann bei einem automatischen Objekt an gleicher Stelle wieder ein Objekt konstruieren (evtl. nicht bei trivialem Destruktor), da das Objekt trotzdem beim Verlassen des Blockes noch einmal zerstört werden wird.



  • Also das mit

    ...
    des(){this->~myclass();}
    ...
    

    funktioniert soweit ganz gut. Aber das mit dem automatischen Objekten hab ich nicht ganz verstanden. Was sind automatische Objekte und warum wird es beim verlassen nochmal zerstört? Kann man das irgendwie unterbinden?



  • Korrektur:
    wenn ich this->~myclass() aufrufe, wird das Objekt nicht zerstört! Ich kann weiterhin darauf zugreifen und bekomme die richtigen Werte!
    Ich hab jetzt noch ein bisschen experimentiert:

    delete this;
    

    macht nur Probleme wenn man das Objekt direkt erstellt und nicht mit new einem Zeiger zuweist!
    Wenn ich ein Objekt mit delete this; zerstöre, kann ich danach immer noch darauf zugreifen, es gibt nur total bescheuerte Werte. Aber auch Funktionsaufrufe funktionieren noch! Erstaunlicherweise geht das auch wenn ich das objekt von außen mit delete objptr; zerstöre.
    Hier mein Exp. Code:

    #include <iostream>
    #include <cstdlib>
    
    using namespace std;
    
    class c { public: int i;};
    
    class tc 
    {
      public:
      int i;
      c *test_c;
      tc()
      {test_c = new c;};
      ~tc(){delete test_c;};
      void des()
      {
        //delete test_c;
        //this->~tc();
        cout<<"a: " <<i<<endl;
        delete this;
      }
    };
    
    int main(int argc, char *argv[])
    {
      tc *test_tc;
      tc *a = 0;
      test_tc = new tc();
      cout << "Hello, world! " << test_tc->i << endl;
      //test_tc->des();
      delete test_tc;
      //delete a;
      test_tc->des();
      cout << test_tc->i << endl;
    
      return EXIT_SUCCESS;
    }
    


  • man kann auf gelöschte objekte noch zugreifen solange der speicher nicht verändert wurde. allerdings kann auch das unvorhersehbare folgen haben...
    hab mal nen artikel über so ein problem gelesen. ich gelaube es war mssql, auf jedenfall iwas von ms. das programm hat immer wieder versucht mit einem gelöschten objekt zu arbeiten, es arbeitete trotzdem stabil. der fehler (obwohl er bekannt war) war ca. 2 jahre aktiv, bis sich zwei studenten daranmachten diesen bug zu exploiten.



  • uekue_ schrieb:

    Korrektur:
    wenn ich this->~myclass() aufrufe, wird das Objekt nicht zerstört! Ich kann weiterhin darauf zugreifen und bekomme die richtigen Werte!

    Doch, das Objekt wird zerstört. Und laut C++ Standard ist es "nicht ok" wenn es danach nochmal zerstört wird -- wenn dazwischen nicht ein neues Objekt an der gleichen Stelle konstruiert wurde (mittels "placement new"). Und das ganz egal was im Destruktor steht oder ob dieser "trivial" ist oder nicht.

    Was du hier beobachtest ist einfach dass nicht alles was laut C++ Standard "nicht ok" ist auch sofort zu einem Fehler/Absturz/... führt.

    Die Frage ist nur ob du dich darauf verlassen willst dass es mit deinem Compiler "eh geht", oder ob du korrekte C++ Programme schreiben willst die mit jedem Compiler funktionieren (der den Standard korrekt implementiert).

    ----

    Am Besten ist du vergisst ganz schnell wieder dass man auch "this->~classname()" schreiben kann.

    "delete this" ist dagegen IMO OK wenn sichergestellt ist dass das Objekt mit "new" erzeugt wurde. Und wird so auch öfters verwendet, z.B. wenn man "intrusive reference counting" implementiert (was auch ohne "delete this" geht, aber oft ist es die einfachste Lösung).



  • Woher soll das Programm eigentlich wissen, wo es nach dem Zerstören weiter arbeiten soll? Daher halte ich Selbst-Zerstörung ohnehin für unlogisch.

    Ausnahmen könnte ich mir höchstens darin denken, dass die zerstörenden Methode statisch wäre oder ein laufender Thread abgeschossen werden soll? Allerdings gäbe es für beides bessere Möglichkeiten.



  • Und wie kann man überprüfen, ob ein Objekte per new erzeugt wurde? Da hilft eigentlich nur, den Konstruktor protected zu machen und eine Factory-Funktion anzubieten.

    Ich hatte mal den Fehler gemacht, und eine Library ein Objekt zerstören zu lassen. Bin dabei beinahe verzweifelt, warum ich Runtime-Exceptions erhielt. Bis ich gesehen habe, das ich das Objekt ja auf dem Stack erzeugt hatte.

    Falls man es überprüfen kann, ob mit new erzeugt, bitte bescheid geben.



  • @crash: Afaik kann die Funktion nach dem 'delete this;' noch weiterarbeiten, solange sie keine internen Daten des Objekts verwendet - sollte aber schnell beendet werden. Und der Aufrufer muß wissen, daß er nach der Zerstörungs-Methode nichts mehr mit dem Objekt machen darf.

    @Artchi: Nein, man kann nicht feststellen, ob die Selbstzerstörung möglich ist (selbst wenn du sicher bist, daß du auf dem Heap liegst, kann das noch im Inneren eines anderen Objekts sein ;)). Die einzige Möglichkeit ist, zu erzwingen, daß alle Objekte per new erzeugt werden (Konstruktor(en) privat definiert und Erzeugung über statische Methoden).



  • Joah, der Ast braucht ja auch noch ne Zeit zwischen Absägen und Aufschlagen 🙄



  • Joah, der Ast braucht ja auch noch ne Zeit zwischen Absägen und Aufschlagen 🙄

    Ich glaub du hast da was nicht richtig verstanden ;):
    Die Methode, von der aus du das Objekt zerstörst, ist ja Teil des (statischen) Programmcodes, bleibt also auch nach dem Zerstören des Objektes bestehen, nur sind eben die Daten des Objektes nicht mehr verfügbar. Ein weiteres Ausführen der Methode ist also gefahrlos möglich...

    Grüße,

    Martin



  • Ok, für alle die's noch nicht so ganz verstanden haben:

    class foo
    {
    public:
        foo() : m_refs(1)
        {
        }
    
        void add_ref() const
        {
            m_refs++;
        }
    
        void release() const
        {
            m_refs--;
            if (m_refs == 0)
                delete this;
        }
    
    private:
        size_t mutable m_refs;
    }
    

    Wenn man den impliziten this Zeiger explizit darstellt sähe das so aus:

    class foo
    {
    // ...
        static void release(foo const* that)
        {
            that->m_refs--;
            if (that->m_refs == 0)
                delete that;
        }
    // ...
    }
    

    Wo ist jetzt das Problem?



  • Also um zu überprüfen ob das Objekt mit new erzeugt wurde, könnte man doch den new Operator für diese Klasse überladen und ein Flag setzten, das dann überprüft wird. Aber wie war das genau mit dem privaten Konstruktor? warum kann ich dann das Objekt mit new erzeugen und anders nicht?



  • uekue_ schrieb:

    Also um zu überprüfen ob das Objekt mit new erzeugt wurde, könnte man doch den new Operator für diese Klasse überladen und ein Flag setzten, das dann überprüft wird. Aber wie war das genau mit dem privaten Konstruktor? warum kann ich dann das Objekt mit new erzeugen und anders nicht?

    Zu dem privaten Konstruktor wird dann noch eine Factory-Methode implentiert, die ein Objekt per new erzeugt und den Pointer zurückliefert.



  • Und die müsste dann statisch sein, damit man sie ohne Objekt aufrufen kann?!?!



  • uekue_ schrieb:

    Und die müsste dann statisch sein, damit man sie ohne Objekt aufrufen kann?!?!

    Genau.



  • Kann so eine Funktion dann über den new Operator das Objekt erzeugen oder muss man dann selbst den Speicher allokieren und den Konstruktor aufrufen?
    also würde so eine Funktion reichen?

    //...
    static MyClass::Create()
    {
       return new MyClass();
    }
    //...
    


  • //...
    static MyClass* MyClass::Create()
    {
       return new MyClass();
    }
    //...
    

    😉

    Und schlau ist es sicher, Smart-Ptr zu benutzen, dann muss sich der Nutzer der Methode nicht um die Freigabe kümmern.



  • Sicher, den Rückgabetypen hab ich wiedermal vergessen, wie so oft bei den Klassenmethoden... Irgendwie ein Fehler den ich jedesmal mache bis sich der Compiler beschwert... 😉
    Aber jetzt nochmal für die Dummen, was sind Smart-Ptr? Wenn sich der Benutzer nicht mehr um die Freigabe kümmern muss, ist das dann sowas wie ein Garbage-Collector bei Java??!?



  • Es ist ein Pointer, der das Objekt was er hält bei gewissen umständen selber löscht (Scope verlassen, keine Referenzen mehr auf das Objekt usw.)

    Im Magazin ist da glaub ich ein Artikel zu, sonst könntest du auch mal bei boost.org vorbeischauen.


Anmelden zum Antworten