Die meisten Leute sagen, wirf niemals eine Ausnahme aus einem Destruktor heraus - dies führt zu undefiniertem Verhalten. Stroustrup weist darauf hin, dass "der Vektordestruktor den Destruktor explizit für jedes Element aufruft. Dies impliziert, dass die Vektorzerstörung fehlschlägt, wenn ein Elementdestruktor ausgelöst wird ... Es gibt wirklich keine gute Möglichkeit, sich vor Ausnahmen zu schützen, die von Destruktoren ausgelöst werden, also die Bibliothek übernimmt keine Garantie, wenn ein Element-Destruktor auslöst "(aus Anhang E3.2) .
Dieser Artikel scheint etwas anderes zu sagen - dass das Werfen von Destruktoren mehr oder weniger in Ordnung ist.
Meine Frage lautet also: Wenn das Werfen von einem Destruktor zu undefiniertem Verhalten führt, wie gehen Sie mit Fehlern um, die während eines Destruktors auftreten?
Wenn während eines Bereinigungsvorgangs ein Fehler auftritt, ignorieren Sie ihn einfach? Wenn es sich um einen Fehler handelt, der möglicherweise im Stapel behandelt werden kann, aber nicht direkt im Destruktor, ist es nicht sinnvoll, eine Ausnahme aus dem Destruktor auszulösen?
Offensichtlich sind solche Fehler selten, aber möglich.
quelle
xyz()
und halte den Destruktor frei von Nicht-RAII-Logik.commit()
Methode aufgerufen wird.Antworten:
Es ist gefährlich, eine Ausnahme aus einem Destruktor herauszuwerfen.
Wenn sich bereits eine andere Ausnahme verbreitet, wird die Anwendung beendet.
Dies läuft im Wesentlichen auf Folgendes hinaus:
Alles, was gefährlich ist (dh eine Ausnahme auslösen könnte), sollte über öffentliche Methoden erfolgen (nicht unbedingt direkt). Der Benutzer Ihrer Klasse kann dann möglicherweise mit diesen Situationen umgehen, indem er die öffentlichen Methoden verwendet und mögliche Ausnahmen abfängt.
Der Destruktor beendet das Objekt dann durch Aufrufen dieser Methoden (falls der Benutzer dies nicht explizit getan hat), aber alle Ausnahmen, die ausgelöst werden, werden abgefangen und gelöscht (nachdem versucht wurde, das Problem zu beheben).
Tatsächlich übertragen Sie die Verantwortung auf den Benutzer. Wenn der Benutzer in der Lage ist, Ausnahmen zu korrigieren, ruft er die entsprechenden Funktionen manuell auf und verarbeitet alle Fehler. Wenn der Benutzer des Objekts nicht besorgt ist (da das Objekt zerstört wird), muss sich der Destruktor um das Geschäft kümmern.
Ein Beispiel:
std :: fstream
Die Methode close () kann möglicherweise eine Ausnahme auslösen. Der Destruktor ruft close () auf, wenn die Datei geöffnet wurde, stellt jedoch sicher, dass keine Ausnahmen aus dem Destruktor übertragen werden.
Wenn der Benutzer eines Dateiobjekts eine spezielle Behandlung für Probleme beim Schließen der Datei durchführen möchte, ruft er close () manuell auf und behandelt alle Ausnahmen. Wenn es ihnen andererseits egal ist, bleibt der Destruktor übrig, um die Situation zu bewältigen.
Scott Myers hat einen ausgezeichneten Artikel zu diesem Thema in seinem Buch "Effective C ++".
Bearbeiten:
Anscheinend auch in "Effective C ++"
Punkt 11: Verhindern Sie, dass Ausnahmen Destruktoren verlassen
quelle
Das Herauswerfen eines Destruktors kann zu einem Absturz führen, da dieser Destruktor möglicherweise als Teil des "Abwickelns des Stapels" aufgerufen wird. Das Abwickeln des Stapels ist eine Prozedur, die ausgeführt wird, wenn eine Ausnahme ausgelöst wird. In dieser Prozedur werden alle Objekte beendet, die seit dem "Versuch" und bis zum Auslösen der Ausnahme in den Stapel geschoben wurden -> ihre Destruktoren werden aufgerufen. Während dieses Vorgangs ist ein weiterer Ausnahmefall nicht zulässig, da nicht zwei Ausnahmen gleichzeitig behandelt werden können. Dies führt zu einem Aufruf von abort (), das Programm stürzt ab und die Steuerung kehrt zum Betriebssystem zurück.
quelle
throw
aber noch keinencatch
Block dafür gefunden hat). In diesem Fall wirdstd::terminate
(nichtabort
) aufgerufen, anstatt eine (neue) Ausnahme auszulösen (oder das Abwickeln des Stapels fortzusetzen).Wir müssen hier differenzieren , anstatt blindlings allgemeinen Ratschlägen für bestimmte Fälle zu folgen .
Beachten Sie, dass im Folgenden das Problem von Containern mit Objekten ignoriert wird und was angesichts mehrerer Objekttypen in Containern zu tun ist. (Und es kann teilweise ignoriert werden, da einige Objekte einfach nicht gut in einen Container passen.)
Das ganze Problem wird leichter zu überlegen, wenn wir Klassen in zwei Typen aufteilen. Ein Klassenleiter kann zwei verschiedene Verantwortlichkeiten haben:
Wenn wir die Frage so sehen, dann kann meiner Meinung nach argumentiert werden, dass (R) -Semantik niemals eine Ausnahme von einem dtor verursachen sollte, da es a) nichts gibt, was wir dagegen tun können, und b) viele Operationen mit freien Ressourcen dies nicht tun sorgen sogar für eine Fehlerprüfung, z .
void
free(void* p);
Objekte mit (C) -Semantik, wie ein Dateiobjekt, das seine Daten erfolgreich leeren muss, oder eine ("bereichsgeschützte") Datenbankverbindung, die ein Commit im dtor ausführt, sind von anderer Art: Wir können etwas gegen den Fehler tun (on die Anwendungsebene) und wir sollten wirklich nicht so weitermachen, als ob nichts passiert wäre.
Wenn wir der RAII-Route folgen und Objekte mit (C) -Semantik in ihren D'tors berücksichtigen, müssen wir meiner Meinung nach auch den seltsamen Fall berücksichtigen, in dem solche D'tors werfen können. Daraus folgt, dass Sie solche Objekte nicht in Container einfügen sollten, und es folgt auch, dass das Programm weiterhin kann,
terminate()
wenn ein Commit-dtor auslöst, während eine andere Ausnahme aktiv ist.In Bezug auf Fehlerbehandlung (Commit / Rollback-Semantik) und Ausnahmen gibt es einen guten Vortrag von einem Andrei Alexandrescu : Fehlerbehandlung in C ++ / Deklarativer Kontrollfluss (gehalten auf der NDC 2014 )
Im Detail erklärt er, wie die Folly-Bibliothek eine
UncaughtExceptionCounter
für ihreScopeGuard
Werkzeuge implementiert .(Ich sollte beachten, dass andere auch ähnliche Ideen hatten.)
Während sich der Vortrag nicht auf das Werfen von einem D'tor konzentriert, zeigt er ein Werkzeug, das heute verwendet werden kann , um die Probleme mit dem Werfen von einem D'tor zu beseitigen.
In
Zukunftgibt esmöglicherweiseeineStandardfunktiondafür,siehe N3614 ,und eine Diskussion darüber .Upd '17: Die C ++ 17-
std::uncaught_exceptions
Standardfunktion hierfür ist afaikt. Ich werde den cppref-Artikel schnell zitieren:quelle
finally
.finally
ist ein dtor. Es heißt immer, egal was passiert. Zur syntaktischen Annäherung von finally siehe die verschiedenen Implementierungen von scope_guard. Heutzutage kann mit der vorhandenen Maschine (selbst im Standard ist es C ++ 14?), Um festzustellen, ob der dtor werfen darf, sogar eine absolute Sicherheit erreicht werden.finally
Natur aus ein Werkzeug für (C) ist. Wenn Sie nicht verstehen, warum: Überlegen Sie, warum es legitim ist, Ausnahmen infinally
Blöcken übereinander zu werfen , und warum dies nicht für Destruktoren gilt. (In gewissem Sinne handelt es sich um eine Daten- / Kontrollsache . Destruktoren dienen zum Freigeben von Daten,finally
zum Freigeben von Kontrolle. Sie sind unterschiedlich; es ist bedauerlich, dass C ++ sie zusammenhält.)Die eigentliche Frage, die Sie sich stellen müssen, wenn Sie von einem Destruktor werfen, lautet: "Was kann der Anrufer damit machen?" Gibt es tatsächlich etwas Nützliches, das Sie mit der Ausnahme tun können, das die Gefahren ausgleichen würde, die durch das Werfen von einem Destruktor entstehen?
Was kann ich vernünftigerweise damit tun, wenn ich ein
Foo
Objekt zerstöre und derFoo
Destruktor eine Ausnahme auslöst? Ich kann es protokollieren oder ignorieren. Das ist alles. Ich kann es nicht "reparieren", da dasFoo
Objekt bereits verschwunden ist. Im besten Fall protokolliere ich die Ausnahme und fahre fort, als wäre nichts passiert (oder beende das Programm). Lohnt es sich wirklich, undefiniertes Verhalten durch Werfen von einem Destruktor zu verursachen?quelle
std::ofstream
Der Destruktor wird geleert und schließt die Datei. Beim Leeren des Datenträgers kann ein Fehler auftreten, bei dem Sie unbedingt etwas Nützliches tun können: Zeigen Sie dem Benutzer einen Fehlerdialog, der besagt, dass auf dem Datenträger nicht mehr genügend Speicherplatz vorhanden ist.Es ist gefährlich, aber es macht auch unter dem Gesichtspunkt der Lesbarkeit / Code-Verständlichkeit keinen Sinn.
Was Sie fragen müssen, ist in dieser Situation
Was soll die Ausnahme fangen? Sollte der Anrufer von foo? Oder sollte foo damit umgehen? Warum sollte sich der Anrufer von foo um ein Objekt innerhalb von foo kümmern? Es mag einen Weg geben, wie die Sprache dies definiert, um Sinn zu machen, aber es wird unlesbar und schwer zu verstehen sein.
Noch wichtiger ist, wohin geht der Speicher für Object? Wohin geht der Speicher, den das Objekt besaß? Ist es noch zugewiesen (angeblich, weil der Destruktor versagt hat)? Bedenken Sie auch, dass sich das Objekt im Stapelbereich befand , sodass es offensichtlich unabhängig davon verschwunden ist.
Dann betrachten Sie diesen Fall
Wenn das Löschen von obj3 fehlschlägt, wie lösche ich dann tatsächlich auf eine Weise, die garantiert nicht fehlschlägt? Es ist meine Erinnerung, verdammt!
Betrachten Sie nun im ersten Code-Snippet, dass Object automatisch verschwindet, da es sich auf dem Stapel befindet, während sich Object3 auf dem Heap befindet. Da der Zeiger auf Object3 weg ist, sind Sie eine Art SOL. Sie haben ein Speicherleck.
Ein sicherer Weg, Dinge zu tun, ist der folgende
Siehe auch diese FAQ
quelle
int foo()
Wenn Sie diese Antwort wiederbeleben , können Sie im ersten Beispiel einen Funktions-Try-Block verwenden, um die gesamte Funktion foo in einen Try-Catch-Block zu verpacken, einschließlich der Catch-Destruktoren, wenn Sie dies möchten. Immer noch nicht der bevorzugte Ansatz, aber es ist eine Sache.Aus dem ISO-Entwurf für C ++ (ISO / IEC JTC 1 / SC 22 N 4411)
Daher sollten Destruktoren im Allgemeinen Ausnahmen abfangen und nicht zulassen, dass sie sich aus dem Destruktor ausbreiten.
quelle
Ihr Destruktor wird möglicherweise innerhalb einer Kette anderer Destruktoren ausgeführt. Das Auslösen einer Ausnahme, die nicht von Ihrem unmittelbaren Anrufer abgefangen wird, kann dazu führen, dass mehrere Objekte in einem inkonsistenten Zustand bleiben, wodurch noch mehr Probleme verursacht werden, als wenn der Fehler beim Bereinigungsvorgang ignoriert wird.
quelle
Ich gehöre zu der Gruppe, die der Ansicht ist, dass das in den Destruktor geworfene "Scoped Guard" -Muster in vielen Situationen nützlich ist - insbesondere für Unit-Tests. Beachten Sie jedoch, dass in C ++ 11 das Einwerfen eines Destruktors zu einem Aufruf von führt,
std::terminate
da Destruktoren implizit mit Anmerkungen versehen sindnoexcept
.Andrzej Krzemieński hat einen großartigen Beitrag zum Thema Destruktoren, die werfen:
Er weist darauf hin, dass C ++ 11 einen Mechanismus hat, um den Standard
noexcept
für Destruktoren zu überschreiben :Wenn Sie sich schließlich dazu entschließen, den Destruktor einzuschalten, sollten Sie sich immer des Risikos einer doppelten Ausnahme bewusst sein (Werfen, während der Stapel aufgrund einer Ausnahme abgewickelt wird). Dies würde einen Anruf bei verursachen
std::terminate
und es ist selten das, was Sie wollen. Um dieses Verhalten zu vermeiden, können Sie einfach überprüfen, ob bereits eine Ausnahme vorliegt, bevor Sie eine neue mit auslösenstd::uncaught_exception()
.quelle
Alle anderen haben erklärt, warum es schrecklich ist, Zerstörer zu werfen ... was können Sie dagegen tun? Wenn Sie einen Vorgang ausführen, der möglicherweise fehlschlägt, erstellen Sie eine separate öffentliche Methode, die eine Bereinigung durchführt und beliebige Ausnahmen auslösen kann. In den meisten Fällen ignorieren Benutzer dies. Wenn Benutzer den Erfolg / Misserfolg der Bereinigung überwachen möchten, können sie einfach die explizite Bereinigungsroutine aufrufen.
Beispielsweise:
quelle
Als Ergänzung zu den Hauptantworten, die gut, umfassend und genau sind, möchte ich zu dem Artikel, auf den Sie verweisen, einen Kommentar abgeben - der besagt, dass "Ausnahmen in Destruktoren zu werfen nicht so schlecht ist".
Der Artikel enthält die Zeile "Was sind die Alternativen zum Auslösen von Ausnahmen?" Und listet einige Probleme mit jeder der Alternativen auf. Nachdem wir dies getan haben, kommt wir zu dem Schluss, dass wir weiterhin Ausnahmen werfen sollten, da wir keine problemlose Alternative finden können.
Das Problem ist, dass keines der darin aufgeführten Probleme mit den Alternativen annähernd so schlimm ist wie das Ausnahmeverhalten, das, wie wir uns erinnern, "undefiniertes Verhalten Ihres Programms" ist. Einige der Einwände des Autors beinhalten "ästhetisch hässlich" und "schlechten Stil fördern". Was hätten Sie lieber? Ein Programm mit schlechtem Stil oder eines mit undefiniertem Verhalten?
quelle
A: Es gibt verschiedene Möglichkeiten:
Lassen Sie die Ausnahmen aus Ihrem Destruktor herausfließen, unabhängig davon, was anderswo vor sich geht. Und seien Sie sich dabei bewusst (oder sogar ängstlich), dass std :: terminate folgen kann.
Lassen Sie niemals Ausnahmen aus Ihrem Destruktor herausfließen. Kann in ein Protokoll schreiben, ein großer roter schlechter Text, wenn Sie können.
Mein Favorit : Wenn
std::uncaught_exception
false zurückgegeben wird, lassen Sie Ausnahmen herausfließen . Wenn true zurückgegeben wird, greifen Sie auf den Protokollierungsansatz zurück.Aber ist es gut, d'tors hineinzuwerfen?
Ich stimme den meisten der oben genannten Punkte zu, dass das Werfen im Destruktor am besten vermieden wird, wo es sein kann. Aber manchmal ist es am besten zu akzeptieren, dass es passieren kann, und gut damit umzugehen. Ich würde 3 oben wählen.
Es gibt einige seltsame Fälle, in denen es eine großartige Idee ist , von einem Destruktor zu werfen. Wie der Fehlercode "muss überprüft werden". Dies ist ein Werttyp, der von einer Funktion zurückgegeben wird. Wenn der Aufrufer den enthaltenen Fehlercode liest / überprüft, wird der zurückgegebene Wert stillschweigend zerstört. Aber , wenn der zurückgegebene Fehlercode durch die Zeit nicht gelesen wurde geht die Rückgabewerte von Rahmen, wird es eine Ausnahme, wirft von seinem destructor .
quelle
Ich folge derzeit der Richtlinie (die so viele sagen), dass Klassen keine aktiven Ausnahmen von ihren Destruktoren auslösen sollten, sondern stattdessen eine öffentliche "Schließen" -Methode bereitstellen sollten, um die Operation auszuführen, die fehlschlagen könnte ...
... aber ich glaube, dass Destruktoren für Klassen vom Containertyp wie ein Vektor keine Ausnahmen maskieren sollten, die von Klassen ausgelöst werden, die sie enthalten. In diesem Fall verwende ich tatsächlich eine "free / close" -Methode, die sich selbst rekursiv aufruft. Ja, sagte ich rekursiv. Es gibt eine Methode für diesen Wahnsinn. Die Weitergabe von Ausnahmen hängt davon ab, dass ein Stapel vorhanden ist: Wenn eine einzelne Ausnahme auftritt, werden beide verbleibenden Destruktoren weiterhin ausgeführt und die ausstehende Ausnahme wird weitergegeben, sobald die Routine zurückkehrt, was großartig ist. Wenn mehrere Ausnahmen auftreten, wird (je nach Compiler) entweder diese erste Ausnahme weitergegeben oder das Programm wird beendet, was in Ordnung ist. Wenn so viele Ausnahmen auftreten, dass die Rekursion den Stapel überläuft, stimmt etwas nicht, und jemand wird es herausfinden, was auch in Ordnung ist. Persönlich,
Der Punkt ist, dass der Container neutral bleibt und es an den enthaltenen Klassen liegt, zu entscheiden, ob sie sich im Hinblick auf das Auslösen von Ausnahmen von ihren Destruktoren verhalten oder schlecht verhalten.
quelle
Im Gegensatz zu Konstruktoren, bei denen das Auslösen von Ausnahmen ein nützlicher Weg sein kann, um anzuzeigen, dass die Objekterstellung erfolgreich war, sollten Ausnahmen nicht in Destruktoren ausgelöst werden.
Das Problem tritt auf, wenn während des Abwickelns des Stapels eine Ausnahme von einem Destruktor ausgelöst wird. In diesem Fall befindet sich der Compiler in einer Situation, in der er nicht weiß, ob er den Stapelabwicklungsprozess fortsetzen oder die neue Ausnahme behandeln soll. Das Endergebnis ist, dass Ihr Programm sofort beendet wird.
Folglich ist die beste Vorgehensweise, auf Ausnahmen bei Destruktoren insgesamt zu verzichten. Schreiben Sie stattdessen eine Nachricht in eine Protokolldatei.
quelle
Martin Ba (oben) ist auf dem richtigen Weg - Sie architektonisch anders für RELEASE- und COMMIT-Logik.
Zur Veröffentlichung:
Sie sollten alle Fehler essen. Sie geben Speicher frei, schließen Verbindungen usw. Niemand im System sollte diese Dinge jemals wieder sehen, und Sie geben Ressourcen an das Betriebssystem zurück. Wenn es so aussieht, als ob Sie hier eine echte Fehlerbehandlung benötigen, ist dies wahrscheinlich eine Folge von Konstruktionsfehlern in Ihrem Objektmodell.
Für Commit:
Hier möchten Sie die gleiche Art von RAII-Wrapper-Objekten, die Dinge wie std :: lock_guard für Mutexe bereitstellen. Mit denen setzen Sie die Commit-Logik überhaupt nicht in den dtor. Sie haben eine dedizierte API dafür, dann Wrapper-Objekte, die RAII in IHRE Dtoren festschreiben und die Fehler dort behandeln. Denken Sie daran, dass Sie Ausnahmen in einem Destruktor problemlos abfangen können. es ist tödlich, sie herauszugeben. Auf diese Weise können Sie auch Richtlinien und unterschiedliche Fehlerbehandlungen implementieren, indem Sie einen anderen Wrapper erstellen (z. B. std :: unique_lock vs. std :: lock_guard), und sicherstellen, dass Sie nicht vergessen, die Festschreibungslogik aufzurufen - dies ist der einzige halbe Weg anständige Rechtfertigung dafür, es an erster Stelle in einen dtor zu setzen.
quelle
Das Hauptproblem ist folgendes: Sie können nicht scheitern, um zu scheitern . Was bedeutet es schließlich, nicht zu scheitern? Was passiert mit der Integrität unserer Daten, wenn das Festschreiben einer Transaktion in eine Datenbank fehlschlägt und nicht fehlschlägt (Rollback fehlschlägt)?
Da Destruktoren sowohl für normale als auch für außergewöhnliche (fehlgeschlagene) Pfade aufgerufen werden, können sie selbst nicht fehlschlagen, oder wir "versagen nicht".
Dies ist ein konzeptionell schwieriges Problem, aber häufig besteht die Lösung darin, nur einen Weg zu finden, um sicherzustellen, dass ein Fehlschlag nicht fehlschlagen kann. Beispielsweise kann eine Datenbank Änderungen schreiben, bevor sie in eine externe Datenstruktur oder Datei übernommen wird. Wenn die Transaktion fehlschlägt, kann die Datei- / Datenstruktur weggeworfen werden. Alles, was es dann sicherstellen muss, ist, dass das Festschreiben der Änderungen von dieser externen Struktur / Datei eine atomare Transaktion ist, die nicht fehlschlagen kann.
Die für mich am besten geeignete Lösung besteht darin, Ihre Nicht-Bereinigungslogik so zu schreiben, dass die Bereinigungslogik nicht fehlschlagen kann. Wenn Sie beispielsweise versucht sind, eine neue Datenstruktur zu erstellen, um eine vorhandene Datenstruktur zu bereinigen, sollten Sie diese Hilfsstruktur möglicherweise im Voraus erstellen, damit wir sie nicht mehr in einem Destruktor erstellen müssen.
Das ist zwar viel leichter gesagt als getan, aber es ist der einzig richtige Weg, den ich sehe. Manchmal denke ich, dass es die Möglichkeit geben sollte, eine separate Destruktorlogik für normale Ausführungspfade zu schreiben, die von außergewöhnlichen entfernt ist, da Destruktoren manchmal das Gefühl haben, dass sie die doppelte Verantwortung haben, wenn sie versuchen, mit beiden umzugehen (ein Beispiel sind Scope Guards, die eine explizite Entlassung erfordern ; sie würden dies nicht benötigen, wenn sie außergewöhnliche Zerstörungspfade von nicht außergewöhnlichen unterscheiden könnten).
Das ultimative Problem ist jedoch, dass wir nicht versagen können, und es ist ein schwieriges Konzeptentwurfsproblem, das in allen Fällen perfekt zu lösen ist. Es wird einfacher, wenn Sie sich nicht zu sehr in komplexe Kontrollstrukturen mit Tonnen von kleinen Objekten einwickeln, die miteinander interagieren, und stattdessen Ihre Entwürfe etwas voluminöser modellieren (Beispiel: Partikelsystem mit einem Destruktor, um das gesamte Partikel zu zerstören System, kein separater nicht trivialer Destruktor pro Partikel). Wenn Sie Ihre Entwürfe auf dieser gröberen Ebene modellieren, müssen Sie sich mit weniger nicht trivialen Destruktoren befassen und können sich häufig den erforderlichen Speicher- / Verarbeitungsaufwand leisten, um sicherzustellen, dass Ihre Destruktoren nicht ausfallen können.
Und das ist natürlich eine der einfachsten Lösungen, Destruktoren seltener zu verwenden. Im obigen Partikelbeispiel sollten möglicherweise beim Zerstören / Entfernen eines Partikels einige Dinge getan werden, die aus irgendeinem Grund fehlschlagen können. In diesem Fall wird stattdessen eine solche Logik durch die Partikel des dtor von Aufrufen , die in einem außergewöhnlichen Weg durchgeführt werden könnte, könnte man stattdessen haben sie alle durch die Partikel - System durchgeführt , wenn es beseitigt ein Teilchen. Das Entfernen eines Partikels kann immer auf einem nicht außergewöhnlichen Weg erfolgen. Wenn das System zerstört wird, kann es möglicherweise nur alle Partikel entfernen und sich nicht mit der einzelnen Partikelentfernungslogik befassen, die fehlschlagen kann, während die Logik, die fehlschlagen kann, nur während der normalen Ausführung des Partikelsystems ausgeführt wird, wenn ein oder mehrere Partikel entfernt werden.
Es gibt oft solche Lösungen, die auftauchen, wenn Sie vermeiden, mit vielen kleinen Objekten mit nicht trivialen Destruktoren umzugehen. Wo Sie sich in einem Chaos verfangen können, in dem es fast unmöglich erscheint, Ausnahmesicherheit zu haben, ist, wenn Sie sich in vielen kleinen Objekten verheddern, die alle nicht triviale Dtoren haben.
Es wäre sehr hilfreich, wenn nothrow / noexcept tatsächlich in einen Compilerfehler übersetzt würde, wenn irgendetwas, das es angibt (einschließlich virtueller Funktionen, die die noexcept-Spezifikation seiner Basisklasse erben sollten), versuchen würde, alles aufzurufen, was auslösen könnte. Auf diese Weise könnten wir all diese Dinge zur Kompilierungszeit abfangen, wenn wir tatsächlich versehentlich einen Destruktor schreiben, der werfen könnte.
quelle
Stellen Sie ein Alarmereignis ein. In der Regel sind Alarmereignisse eine bessere Form, um Fehler beim Bereinigen von Objekten zu melden
quelle