Destructor wird nicht aufgerufen, wenn eine Exception im Konstruktor ausgelöst wird

8

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%     
cppcoder 02.04.2012, 06:32
quelle

5 Antworten

19

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 von scoped_ptr oder über ein explizites reset

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

     
  1. void beenden ();
  2.   

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, bevor terminate() aufgerufen wird. Eine Implementierung ist nicht dazu berechtigt, die Stapelabwickelung vorzeitig zu beenden, basierend auf einer Bestimmung, dass der Abwicklungsvorgang schließlich einen Aufruf von terminate() verursachen wird.

    
Alok Save 02.04.2012, 06:58
quelle
7

Der Aufruf des Destruktors ist in diesem Fall nicht sinnvoll.

Du zerstörst nur Dinge, die konstruiert sind, doch dein Objekt wird niemals vollständig konstruiert. Ihre Klasse -Elemente wurden jedoch erstellt und ihre Destruktoren werden aufgerufen.

    
GManNickG 02.04.2012 06:36
quelle
0

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.

    
iammilind 02.04.2012 06:37
quelle
0

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.

>     
Hurkyl 02.04.2012 06:44
quelle
0

Wenn ein Konstruktor eine Ausnahme auslöst, wird der Destruktor der Klasse nicht aufgerufen, weil das Objekt nicht vollständig konstruiert ist.

Siehe diesen Link zum Verwalten von Ressourcen in einer solchen Situation:

Ссылка

    
Sanish 02.04.2012 06:44
quelle

Tags und Links