Kann sich ein Objekt selbst zerstören?
-
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.
-
Ein Objekt kann sich selbst löschen, in der MFC wird das z.B. für die Notizen verwendet, die eingeblendet werden.
*schauer über den Rücken jag*
Das Dämliche an der Stelle ist nur, man weiß nie wann die Pointer auf so ein
Objekt ungültig wird, referenzieren/verweisen ist also ganz schlecht.
Man erzeugt sich damit für gewöhnlich schöne sporadisch auftretende Abstürze, wenn nicht garantiert werden kann das alle Pointer 100% auf Null gesetzt werden und das Objekt die Lösch-Funktion nicht selbst indirekt irgendwie aufruft (dann zerbröselt es die Anwendung auch).
Meiner Meinung nach erspart man sich am Besten diesen Ärger gleich und implementiert eine zentrale Instanz für die Verwaltung der Objekte. Das ist viel robuster.
-
MathiasTemp schrieb:
...
Sofern du dich jetzt mit deiner Aussage nicht ausschließlich auf die Microsoft-Bereiche beschränkst, ist sie IMHO völliger Unsinn.
Sofern man dafür sorgt das man konsequent mit Smartpointern arbeitet, ist immer der Zeitpunkt der Zerstörung garantiert, oder kann zumindest sinnvoll gegengeprüft werden (z.B. bei boost::scoped_ptr, oder der Kombination von boost::shared_ptr/boost::weak_ptr).
cu André
-
wer new sagt, sagt auch delete. 'nuff said.
-
Ob in einem Code "delete this" vorkommt oder nicht ist letztlich vollkommen egal. Man kann jeden Code der "delete this" verwendet so umschreiben dass es (zumindest "wörtlich") nichtmehr vorkommt, was aber IMO oft keinen Unterschied macht bzw. die Dinge sogar ... z.T. "undurchsichtiger" macht als notwendig.
Was hier diskutiert ist ist IMO weniger ob "delete this" OK ist, sondern ob gewisse Techniken/Patterns OK sind. Wie z.B. "intrusive reference counting" oder "detached objects" (mit "detached objects" meine ich hier Objekte die irgendwas tun und sich danach selbst zerstören, d.h. auf die man von Aussen keine Zeiger halten "sollte", und wenn doch eben irgendwoher wissen muss dass das Objekt sich zu dem Zeitpunkt wo man den Zeiger verwendet noch nicht selbst zerstört haben kann. Analog zu "detached threads" eben).
IMO gibt es durchaus Anwendungsfälle wo diese Techniken/Patterns gut und problemlos eingesetzt werden können. Natürlich können fast alle Techniken/Patterns zu Problemen führen wenn man sie "falsch" einsetzt, was diese aber nicht grundsätzlich schlecht oder gefährlich macht.