Warum wird der Destruktor in diesem Code nicht aufgerufen?
%Vor%BEARBEITEN
Aus den Antworten verstehen wir, dass der Destruktor nicht aufgerufen wird, wenn im Konstruktor eine Ausnahme auftritt. Wenn die Ausnahme jedoch in main () auftritt, dh nachdem das MyClass-Objekt vollständig instanziiert wurde, wird der MyClass-Destruktor aufgerufen? Wenn nicht, warum ist es dann ein intelligenter Zeiger?
Hinzufügen des Codes
%Vor%Ausgabe:
%Vor% Die Lebensdauer eines C ++ - Objekts beginnt erst, nachdem der Konstruktor erfolgreich abgeschlossen wurde.
Da die Ausnahme ausgelöst wurde, bevor der Konstruktoraufruf abgeschlossen wurde, haben Sie kein vollständiges Objekt und daher auch keinen Destruktor.
Herb Sutter erklärt dies nett , um ihn zu zitieren:
F: Was bedeutet das Auslösen einer Ausnahme von einem Konstruktor?
A: Es bedeutet, dass die Konstruktion gescheitert ist, das Objekt hat nie existiert, seine Lebensdauer hat nie begonnen. Tatsächlich ist die einzige Möglichkeit, das Versagen der Konstruktion zu melden - das heißt, die Unfähigkeit, ein funktionierendes Objekt des gegebenen Typs korrekt zu bauen - eine Ausnahme auszulösen. (Ja, es gibt eine inzwischen veraltete Programmierkonvention, die besagt: "Wenn Sie in Schwierigkeiten geraten, setzen Sie einfach eine Statusmarkierung auf" schlecht "und lassen Sie den Anrufer dies über eine IsOK () - Funktion überprüfen." Ich werde das jetzt kommentieren .)
In biologischer Hinsicht fand die -Konzeption statt - der Konstrukteur begann - aber trotz aller Bemühungen folgte eine Fehlgeburt - der Konstrukteur lief nie zu Ende (ination). em>
Übrigens: deshalb wird ein Destruktor niemals aufgerufen, wenn der Konstruktor nicht erfolgreich war - es gibt nichts zu zerstören.
"It cannot die, for it never lived."
Beachten Sie, dass dies den Ausdruck"an object whose constructor threw an exception"
wirklich zu einem Oxymoron macht. So etwas ist noch weniger als ein Ex-Objekt ... es hat nie gelebt, war nie, hat nie sein erstes geatmet. Es ist ein Nicht-Objekt.Wir könnten das C ++ - Konstruktormodell wie folgt zusammenfassen:
Entweder:
(a) Der Konstruktor kehrt normal zurück, indem er sein Ende oder eine return-Anweisung erreicht, und das Objekt existiert.
Oder:
(b) Der Konstruktor wird beendet, indem eine Ausnahme ausgegeben wird, und das Objekt existiert jetzt nicht nur, sondern existierte nie als Objekt.
EDIT 1:
Aber wenn die Ausnahme in main()
auftritt, dh nachdem das Objekt MyClass
vollständig instanziiert wurde, wird der MyClass
destructor aufgerufen?
Ja, wird es sein!
Dies ist der Zweck der Verwendung von scoped_ptr
. Sobald eine Ausnahme in main
geworfen wird, würde das Entpacken des Stacks dazu führen, dass alle lokalen Objekte freigegeben werden. Dies bedeutet, dass myinst
(das sich auf dem Stack befindet) ebenfalls freigegeben wird Turn ruft den Destruktor von MyClass
auf.
Lesen Sie die Verbesserung der Dokumentation , wenn Sie angemeldet sind Zweifel:
Die Klassenvorlage
scoped_ptr
speichert einen Zeiger auf ein dynamisch zugewiesenes Objekt. (Dynamisch allozierte Objekte werden mit dem neuen C ++ - Ausdruck zugeordnet.) Das Objekt, auf das gezeigt wird, wird garantiert gelöscht, entweder bei der Zerstörung vonscoped_ptr
oder über ein explizitesreset
BEARBEITEN 2:
Warum stürzt Ihr bearbeitetes Programm ab?
Ihr Programm zeigt Abstürze an, weil Sie eine Ausnahme auslösen, die Sie aber nie fangen. Wenn ein solches Szenario auftritt, wird eine spezielle Funktion namens terminate()
aufgerufen, deren Standardverhalten abort()
aufruft. Es ist ein implementierungsdefiniertes Verhalten, ob der Stapel Unwinded ist, bevor terminate()
in diesem speziellen Szenario aufgerufen wird Ref 1 .Scheint Ihre Implementierung nicht & amp; Sie sollten sich auch nicht auf dieses Verhalten verlassen.
Sie können Ihr Programm wie folgt ändern, um die Ausnahme zu behandeln, und Sie sollten das Verhalten erhalten, das Sie erwartet haben:
%Vor%Ref 1 C ++ 03 15.5.1 Das terminieren () Funktion
In den folgenden Situationen muss die Ausnahmebehandlung für weniger subtile Fehlerbehandlungstechniken aufgegeben werden:
....
- wenn der Ausnahmebehandlungsmechanismus keinen Handler für eine ausgelöste Ausnahme finden kann (15.3),
....In solchen Fällen
- void beenden ();
heißt (18.6.3). In der Situation, in der kein passender Handler gefunden wird, ist in der Implementierung definiert, ob der Stapel abgewickelt wird, bevor
terminate()
aufgerufen wird. In allen anderen Situationen darf der Stapel nicht abgewickelt werden, bevorterminate()
aufgerufen wird. Eine Implementierung ist nicht dazu berechtigt, die Stapelabwickelung vorzeitig zu beenden, basierend auf einer Bestimmung, dass der Abwicklungsvorgang schließlich einen Aufruf vonterminate()
verursachen wird.
Wenn die Ausnahme vom Konstruktor ausgelöst wird (am Anfang oder auf halbem Weg oder am Ende des Aufrufs), ist sichergestellt, dass das Objekt nicht konstruiert ist.
Es ist also gut definiert, den Destruktor eines Objekts, das niemals konstruiert wurde, nicht aufzurufen.
Hier ist eine verwandte FAQ von Bjarne's Website.
Der Destruktor für MyClass
wurde nie aufgerufen, da niemals Objekte vom Typ MyClass
erstellt wurden. Jeder Versuch, einen zu erstellen, wurde abgebrochen, da die Ausnahme ausgelöst wurde.
Wenn Sie möchten, dass Ihre Debug-Meldungen angezeigt werden - vor allem, wenn Sie mit dem Absturz des Programms zu tun haben - sollten Sie wirklich die Streams leeren, dh stattdessen std::endl
verwenden von '\n'
am Ende der Zeile. (oder Einfügen von std::flush
)
Obwohl die Verwendung von '\n'
oft funktioniert, gibt es genug Situationen, in denen es fehlschlägt und es ist wirklich wirklich verwirrend zu debuggen, wenn Sie nicht die Gewohnheit haben, die Dinge richtig zu machen.
Tags und Links c++ boost scoped-ptr