Werden Destruktoren nach einem Wurf in C++ aufgerufen?

Lesezeit: 5 Minuten

Werden Destruktoren nach einem Wurf in C aufgerufen
Luchian Grigore

Ich habe ein Beispielprogramm ausgeführt und tatsächlich werden Destruktoren für stapelzugeordnete Objekte aufgerufen, aber wird dies vom Standard garantiert?

  • Sicher ist es das. RAII, eines der wichtigsten Idiome in C++, hängt davon ab.

    – Jon

    29. November 11 um 13:32 Uhr

  • Ja, das ist der springende Punkt bei der Ausnahmebehandlung.

    – Kerrek SB

    29. November 11 um 13:40 Uhr

  • @Jon & Kerrek SB, Wenn die Ausnahme nicht abgefangen wird, ist das Abwickeln des Stapels nicht garantiert, es ist die Implementierung definiert: Siehe die Antwort von NPE unten, der letzte Teil ist das Zitat aus dem Standard, der dies besagt.

    – Philippe Carphin

    17. September 2020 um 4:56 Uhr

Werden Destruktoren nach einem Wurf in C aufgerufen
NPE

Ja, es ist garantiert (sofern die Ausnahme abgefangen wird), bis auf die Bestellung in dem die Destruktoren aufgerufen werden:

C++11 15.2 Konstruktoren und Destruktoren [except.ctor]

1 Wenn die Kontrolle von einem Throw-Ausdruck an einen Handler übergeht, werden Destruktoren für alle automatischen Objekte aufgerufen, die seit der Eingabe des try-Blocks erstellt wurden. Die automatischen Objekte werden in umgekehrter Reihenfolge ihrer Fertigstellung zerstört.

Wenn die Ausnahme während der Objektkonstruktion ausgelöst wird, werden die Unterobjekte des teilweise konstruierten Objekts außerdem garantiert korrekt zerstört:

2 Bei einem Objekt mit beliebiger Speicherdauer, dessen Initialisierung oder Zerstörung durch eine Ausnahme beendet wird, werden Destruktoren für alle seine vollständig konstruierten Unterobjekte ausgeführt (mit Ausnahme der Variantenmitglieder einer unionsähnlichen Klasse), d. h. für Unterobjekte, für die der Hauptkonstruktor (12.6.2) die Ausführung abgeschlossen hat und der Destruktor noch nicht mit der Ausführung begonnen hat. Wenn der nicht delegierende Konstruktor für ein Objekt die Ausführung abgeschlossen hat und ein delegierender Konstruktor für dieses Objekt mit einer Ausnahme beendet wird, wird der Destruktor des Objekts aufgerufen. Wenn das Objekt in einem Neu-Ausdruck zugewiesen wurde, wird die übereinstimmende Freigabe-Funktion (3.7.4.2, 5.3.4, 12.5), falls vorhanden, aufgerufen, um den durch das Objekt belegten Speicher freizugeben.

Dieser gesamte Vorgang wird als “Stack Unwinding” bezeichnet:

3 Der Vorgang des Aufrufens von Destruktoren für automatische Objekte, die auf dem Pfad von einem Try-Block zu einem Throw-Ausdruck erstellt werden, wird als „Stack Unwinding“ bezeichnet. Wenn ein Destruktor, der beim Entladen des Stapels aufgerufen wird, mit einer Ausnahme beendet wird, wird std::terminate aufgerufen (15.5.1).

Die Stapelabwicklung bildet die Grundlage der weit verbreiteten so genannten Technik Ressourcenerfassung ist Initialisierung (RAII).

Beachten Sie, dass das Stack-Unwinding nicht unbedingt durchgeführt wird, wenn die Ausnahme nicht abgefangen wird. In diesem Fall ist es Sache der Implementierung, ob ein Stack-Unwinding durchgeführt wird. Aber unabhängig davon, ob der Stapel abgewickelt wird oder nicht, in diesem Fall ist Ihnen ein letzter Anruf garantiert std::terminate.

C++11 15.5.1 Die Funktion std::terminate() [except.terminate]

2 … In der Situation, in der kein passender Handler gefunden wird, ist es implementierungsdefiniert, ob der Stack vorher abgewickelt wird oder nicht std::terminate() wird genannt.

  • Hinweis: bezüglich der Konstruktion eines unterbrochenen Objekts. Das Objekt selbst wird nicht zerstört (es hat nie wirklich gelebt), was garantiert ist, dass die bisher vollständig konstruierten Unterteile (Basisklassen, Attribute) in umgekehrter Reihenfolge zerstört werden.

    – Matthias M.

    29. November 11 um 14:55 Uhr

  • Informationen zum Stack-Unwinding (oder nicht) für nicht abgefangene Ausnahmen hinzugefügt.

    – Prost und hth. – Alf

    29. November 11 um 15:31 Uhr

Ja, Destruktoren werden garantiert beim Entladen des Stapels aufgerufen, einschließlich des Entladens aufgrund einer ausgelösten Ausnahme. Es gibt nur wenige Tricks mit Ausnahmen, die Sie sich merken müssen:

  • Der Destruktor der Klasse wird nicht aufgerufen, wenn in seinem Konstruktor eine Ausnahme ausgelöst wird.
  • Die Ausnahme wird automatisch erneut ausgelöst, wenn sie im Catch-Block der Konstruktionsinitialisierungsliste abgefangen wird.

  • 3) Destruktoren sollten noch nie Ausnahmen werfen, wie es gibt Nein Art und Weise, sie angemessen zu behandeln.

    – DevSolar

    29. November 11 um 13:39 Uhr

  • @DevSolar Es gibt einige Gegenbeispiele.

    – Prost und hth. – Alf

    29. November 11 um 15:32 Uhr

  • @AlfP.Steinbach: Jeder Destruktor, der während des Stack-Unwinding geworfen wird (aufgrund von Ein weiterer Ausnahme ausgelöst wird) wird terminate() Ihr Prozess. Mich würden Gegenbeispiele interessieren…

    – DevSolar

    29. November 11 um 15:52 Uhr

  • @DevSolar: Sie sind sich (absichtlich?) nicht sicher, wozu Sie Gegenbeispiele wünschen. Aber in Bezug auf die erste Behauptung, dass “Destruktoren niemals Ausnahmen auslösen sollten”, ist ein nicht ganz ungewöhnliches Gegenbeispiel ein Objekt, das ein Funktionsergebnis darstellt und das von seinem Destruktor auslöst, wenn der Aufrufercode nicht überprüft hat, ob es einen Fehler darstellt. Ein weiteres Beispiel ist ein Transaktionsschutzobjekt, das von seinem Destruktor wirft, es sei denn, der Code, in den es eingebettet ist, hat seine Bemühungen erfolgreich abgeschlossen (z. B. den Besitz von etwas zu übertragen) und es aufgerufen release Methode.

    – Prost und hth. – Alf

    29. November 11 um 16:00 Uhr


  • @AlfP.Steinbach: “Nutzungscode kann darum herum entworfen werden.” – Ich stimme zu. Als Faustregel gilt jedoch, dass Ihre Objekte möglicherweise in einem Kontext verwendet werden, den Sie nicht vorhergesehen haben, und nicht proaktiv berücksichtigt werden können. Und wenn Sie z. B. destruktorauslösende Objekte in einen STL-Vektor einfügen und dieser Vektor aufgrund einer nicht verwandten Ausnahme-Stack-Abwicklung zerstört wird, ruft er den Destruktor Ihres Objekts auf, und Ihre Anwendung macht “Puff”. Sicher, ich kann den Stift von einer Granate ziehen, eine Weile vorsichtig damit umgehen und den Stift wieder hineinstecken. Aber ich nie sollen TU das…

    – DevSolar

    29. November 11 um 16:54 Uhr

Wenn ein Wurf abgefangen wird, werden die CPP-Operationen normalerweise fortgesetzt. Dazu gehören Destruktoren und Stack-Popping. Wenn die Ausnahme jedoch nicht abgefangen wird, ist das Stack-Popping nicht garantiert.

Auch ein bloßer Wurf oder ein leerer Wurf kann von meinem mobilen Compiler nicht abgefangen werden.

Beispiel:

#include <Jav/report.h>

int main()
{
 try { throw; }
 catch(...) { rep("I bet this is not caught"); }
 }

  • Dafür habe ich ein Plus gegeben. Es wird nicht nur nicht abgefangen, sondern Destruktoren für automatische Objekte im try-Block (einfach genug, um einen einzufügen) werden nicht aufgerufen (g++ 7.4.0/clang++ 6.0.0 ubuntu), -std=c++[11|14|17]. Scheint nicht zu helfen, noexcept mit der Hauptdeklaration zu verwenden. Ich habe auch entsprechende Manpage-Optionen ausprobiert. Ich kann eine Warnung für einen bloßen Wurf in einem Catch-Block erhalten, aber nicht in einem Try-Block. Falls ich etwas übersehe, bitte um Aufklärung.

    – Davernator

    25. Juli 19 um 22:23 Uhr


  • @davernator Danke für dein Plus. Ich hoffe, Sie haben nichts übersehen, da dies über meiner Gehaltsstufe liegt. Selbst jetzt. Entspann dich.

    Benutzer9599745

    13. März 20 um 4:08 Uhr


.

758570cookie-checkWerden Destruktoren nach einem Wurf in C++ aufgerufen?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy