Frage

Die meisten Leute sagen nie eine Ausnahme von einem Destruktor werfen - so ergibt undefiniertes Verhalten zu tun. Stroustrup den Punkt macht, dass "der Vektor destructor ausdrücklich destructor für jedes Element aufruft. Dies impliziert, dass, wenn ein Element destructor wirft, schlägt die Vektor Zerstörung ... Es gibt wirklich keine gute Art und Weise gegen Ausnahmen von Destruktoren geworfen zu schützen, so macht die Bibliothek keine Garantie, wenn ein Element destructor wirft“(aus Anhang E3.2) .

dieser Artikel scheint anders zu sagen - das werfen Destruktoren sind mehr oder weniger in Ordnung.

Also meine Frage ist - wenn von einem destructor Ergebnis in undefiniertem Verhalten zu werfen, wie Sie Fehler gehen, die während eines destructor auftreten

?

Wenn ein Fehler während einer Bereinigungsoperation auftritt, ignorieren Sie es gerade? Wenn es einen Fehler, die möglicherweise den Stapel gehandhabt werden können, aber nicht direkt im destructor, macht es keinen Sinn machen, eine Ausnahme aus dem destructor zu werfen?

Offensichtlich sind diese Arten von Fehlern sind selten, aber möglich.

War es hilfreich?

Lösung

Werfen einer Ausnahme von einem Destruktor ist gefährlich.
Wenn eine andere Ausnahme ist bereits die Anwendung ausbreitende wird beendet.

#include <iostream>

class Bad
{
    public:
        // Added the noexcept(false) so the code keeps its original meaning.
        // Post C++11 destructors are by default `noexcept(true)` and
        // this will (by default) call terminate if an exception is
        // escapes the destructor.
        //
        // But this example is designed to show that terminate is called
        // if two exceptions are propagating at the same time.
        ~Bad() noexcept(false)
        {
            throw 1;
        }
};
class Bad2
{
    public:
        ~Bad2()
        {
            throw 1;
        }
};


int main(int argc, char* argv[])
{
    try
    {
        Bad   bad;
    }
    catch(...)
    {
        std::cout << "Print This\n";
    }

    try
    {
        if (argc > 3)
        {
            Bad   bad; // This destructor will throw an exception that escapes (see above)
            throw 2;   // But having two exceptions propagating at the
                       // same time causes terminate to be called.
        }
        else
        {
            Bad2  bad; // The exception in this destructor will
                       // cause terminate to be called.
        }
    }
    catch(...)
    {
        std::cout << "Never print this\n";
    }

}

Diese im Grunde läuft darauf hinaus:

Alles, was gefährlich (das heißt, die eine Ausnahme auslösen könnte) sollte über öffentliche Methoden (nicht unbedingt direkt) erfolgen. Der Benutzer Ihrer Klasse kann dann möglicherweise mit diesen Situationen umgehen, indem sie die öffentlichen Methoden und mögliche Ausnahmen zu kontrollieren.

Der Destruktor wird dann beenden Sie das Objekt durch diese Methoden aufrufen (wenn der Benutzer nicht so explizit zu tun hat), aber Ausnahmen werfen gefangen und sank (nach dem Versuch, das Problem zu beheben).

Also in der Tat Sie die Verantwortung auf den Nutzer übergeben. Wenn der Benutzer in der Lage ist, Ausnahmen zu korrigieren werden sie manuell die entsprechenden Funktionen aufrufen und verarbeitet alle Fehler. Wenn der Benutzer des Objekts nicht besorgt (wie das Objekt zerstört wird), dann wird der destructor links Betreuung von Unternehmen zu übernehmen.

Ein Beispiel:

std :: fstream

Die Methode close () kann möglicherweise eine Ausnahme aus. Der Destruktor ruft close (), wenn die Datei geöffnet wurde, sondern sorgt dafür, dass alle Ausnahmen aus dem destructor nicht fortpflanzen.

Also, wenn der Benutzer eines Dateiobjekt will spezielle Handhabung für Probleme Schließen der Datei zugeordnet ist zu tun, wird sie manuell schließen () aufrufen und alle Ausnahmen zu behandeln. Wenn auf der anderen Seite sie dich nicht, dann wird der destructor verlassen werden, um die Situation zu behandeln.

Scott Myers hat einen ausgezeichneten Artikel über das Thema in seinem Buch "Effective C ++"

Edit:

Offenbar auch in "More Effective C ++"
Artikel 11: Verhindern, dass Ausnahmen von Destruktoren verlassen

Andere Tipps

kann zu einem Absturz führen aus einem destructor Werfen, weil diese destructor als Teil der „Stack Abwickeln“ genannt werden könnte. Stack Abwickeln ist ein Verfahren, das stattfindet, wenn eine Ausnahme ausgelöst wird. Bei diesem Verfahren werden alle Objekte, die in den Stapel, da die „versuchen“, und bis die Ausnahme ausgelöst wurde gedrückt wurden, wird beendet -> ihre Destruktoren aufgerufen werden. Und während dieses Verfahrens eine weitere Ausnahme throw nicht erlaubt ist, weil es nicht möglich ist, zwei Ausnahmen zu einem Zeitpunkt, zu handhaben, so wird dieser Anruf provozieren () abbrechen, wird das Programm zum Absturz bringen und die Steuerung an das Betriebssystem zurück.

Wir müssen differenzierbare hier statt blind zu folgen allgemeine Beratung für spezifische Fälle.

Hinweis

, dass die folgende ignoriert die Ausgabe von Containern von Objekten und was angesichts mehrerer d'toren von Objekten in Containern zu tun. (Und kann es teilweise ignoriert werden, da einige Objekte nur keine gute Passform sind in einen Behälter zu setzen.)

Das ganze Problem wird einfacher zu denken, wenn wir uns trennen Klassen in zwei Typen. Eine Klasse dtor können zwei verschiedene Aufgaben:

  • (R) Release Semantik (aka frei, dass der Speicher)
  • (C) commit Semantik (auch bekannt als flush Datei auf der Festplatte)

Wenn wir die Frage auf diese Weise sehen, dann denke ich, dass es kann argumentiert werden, dass (R) Semantik sollte niemals eine Ausnahme von einer dtor verursachen, da es a) nichts wir dagegen tun können und b) viele Frei Ressource Operationen zur Verfügung stellen nicht einmal für die Fehlerprüfung, zB void free(void* p);.

Objekte mit (C) Semantik, wie ein Datei-Objekt, das erfolgreich spülen muss seine Daten oder eine ( „scope bewacht“) Datenbank-Verbindung, die eine im dtor tut begehen, sind von anderer Art: We können etwas über den Fehler machen (auf der Anwendungsebene), und wir sollten wirklich nicht weiter, als ob nichts passiert ist.

Wenn wir folgen dem RAII Weg und lassen für Objekte, die haben (C) Semantik in ihren d'toren Ich denke, wir dann müssen auch für die ungeraden Fall ermöglichen, in denen solche d'toren werfen. Daraus folgt, dass Sie nicht solche Gegenstände in einem Behälter sollen und es folgt auch, dass das Programm noch terminate() wenn ein Commit-dtor wirft, während ein andere Ausnahme aktiv ist.


Bei der Handhabung auf Fehler (Commit / Rollback Semantik) und Ausnahmen gibt es ein gutes Gespräch mit einem Andrei Alexandrescu : Fehlerbehandlung in C ++ / deklarativem Kontrollfluss (gehalten bei NDC 2014 )

In den Einzelheiten erläutert er, wie die Folly Bibliothek eine UncaughtExceptionCounter für ihre ScopeGuard Tooling.

(Ich sollte anmerken, dass andere auch ähnliche Ideen hatten.)

Während die Rede von einem d'tor konzentriert sich nicht auf das Werfen es ein Werkzeug zeigt, die verwendet werden können heute , um loszuwerden, der Probleme mit, wenn sie von einem d'tor werfen .

Im Zukunft gibt können ein std Merkmal dafür sein, sehen N3614 , und ein Diskussion darüber .

Upd '17: Das C ++ 17 std Merkmal hierfür ist std::uncaught_exceptions afaikt. Ich werde den cppref Artikel schnell zitieren:

  

Notizen

     

Ein Beispiel, wo int-Rückkehr uncaught_exceptions verwendet wird, ist ... ... zuerst   Objekt schafft eine Wache und zeichnet die Anzahl der nicht abgefangene Ausnahmen   in seinem Konstruktor. Der Ausgang wird durch den Schutz Objekt durchgeführt   destructor, es sei denn foo () throws ( in diesem Fall die Anzahl der abgefangene   Ausnahmen in der destructor ist größer als das, was der Konstruktor   beobachtet )

Die eigentliche Frage, sich zu fragen etwa von einem destructor werfen ist: „Was kann mit diesem der Anrufer tun?“ Gibt es Sie tatsächlich etwas Sinnvolles mit Ausnahme machen kann, dass die Gefahren durch das Werfen von einem Destruktor erstellt Offset würde?

Wenn ich ein Foo Objekt zu zerstören, und der Foo destructor wirft eine Ausnahme aus, was kann ich vernünftig mit ihm tun? Ich kann es sich einzuloggen, oder ich kann es ignorieren. Das ist alles. Ich kann nicht „reparieren“ es, weil das Foo Objekt ist schon weg. Im besten Fall, log ich die Ausnahme und weiter, als ob nichts passiert ist (oder das Programm beenden). Ist das wirklich wert möglicherweise nicht definiertes Verhalten verursacht durch von einem destructor werfen?

Sein gefährlich, aber es macht auch keinen Sinn, von einer Ablesbarkeit / code Verständlichkeits Standpunkt machen.

Was muss man fragen, in dieser Situation ist,

int foo()
{
   Object o;
   // As foo exits, o's destructor is called
}

Was sollte die Ausnahme fangen? Sollte der Anrufer von foo? Oder sollte foo damit umgehen? Warum sollte der Anrufer von foo kümmern sich um einige Objekt intern foo? Es könnte ein Weg sein, die Sprache dieses sinnvoll definiert, aber seine unlesbar und schwierig sein wird, zu verstehen.

Noch wichtiger ist, wo sich der Speicher für Objekt gehen? Wo kommt das Objekt der Speicher im Besitz gehen? Ist es immer noch zugeteilt (angeblich, weil der Destruktor nicht)? Beachten Sie auch das Objekt war in Stapelspeicher , so dass sie offensichtlich unabhängig gegangen.

Dann diesen Fall betrachten

class Object
{ 
   Object2 obj2;
   Object3* obj3;
   virtual ~Object()
   {
       // What should happen when this fails? How would I actually destroy this?
       delete obj3;

       // obj 2 fails to destruct when it goes out of scope, now what!?!?
       // should the exception propogate? 
   } 
};

Wenn die Löschen von obj3 ausfällt, wie lösche ich tatsächlich in einer Weise, die nicht versagen garantiert ist? Sein Gedächtnis dammit!

Jetzt in dem ersten Code-Schnipsel Objekt betrachtet weggeht automatisch, weil sein auf dem Stapel, während Objekt3 auf dem Heap ist. Da der Zeiger auf Objekt3 ist weg, du bist Art von SOL. Sie haben einen Speicherverlust.

Jetzt eine sichere Art und Weise, Dinge zu tun, ist das folgende

class Socket
{
    virtual ~Socket()
    {
      try 
      {
           Close();
      }
      catch (...) 
      {
          // Why did close fail? make sure it *really* does close here
      }
    } 

};

Siehe auch diese FAQ

Von der ISO Entwurf für C ++ (ISO / IEC JTC 1 / SC 22 N 4411)

So Destruktoren Regel Ausnahmen fangen sollte und nicht zulassen, sie aus dem destructor propagieren.

  

3 Das Verfahren von Destruktoren für automatische Objekte aufgebaut auf dem Weg von einem try-Block zu einem Wurf- Aufruf     Ausdruck „Stack Abwickeln“ genannt. [Anmerkung: Wenn ein destructor während Stack Abwickeln Ausfahrten mit einem genannt     Ausnahme, std :: beenden aufgerufen wird (15.5.1). So Destruktoren sollte in der Regel Ausnahmen fangen und nicht zulassen,     sie propagieren aus dem destructor. - Endnote]

Ihr destructor könnte innerhalb einer Kette von anderen Destruktoren wird ausgeführt wird. Auslösen einer Ausnahme, die von Ihrem unmittelbaren Aufrufer nicht gefangen wird, um mehrere Objekte in einem inkonsistenten Zustand hinterlassen, wodurch noch mehr Probleme dann den Fehler in der Bereinigungsoperation ignoriert.

Jeder andere hat erklärt, warum werfen Destruktoren sind schrecklich ... was kann man dagegen tun? Wenn Sie eine Operation tun, die eine separate öffentliche Methode, die Bereinigung führt fehlschlagen, erstellen und beliebige Ausnahmen auslösen kann. In den meisten Fällen werden die Nutzer, dass ignorieren. Wenn Benutzer den Erfolg / Misserfolg der Bereinigung überwachen wollen, können sie einfach die explizite Bereinigungsroutine aufrufen.

Zum Beispiel:

class TempFile {
public:
    TempFile(); // throws if the file couldn't be created
    ~TempFile() throw(); // does nothing if close() was already called; never throws
    void close(); // throws if the file couldn't be deleted (e.g. file is open by another process)
    // the rest of the class omitted...
};

Als Ergänzung zu den wichtigsten Antworten, die gut sind, umfassende und genaue, ich über den Artikel kommentieren möchten Sie verweisen -. Derjenige, der sagt: „werfen Ausnahmen in Destruktoren ist nicht so schlecht“

Der Artikel nimmt die Linie „was die Alternativen zu werfen Ausnahmen sind“, und listet mit jeder der Alternativen einige Probleme. Nachdem dies erledigt, so dass es, dass kommt zu dem Schluss, weil wir kein Problem freie Alternative finden wir werfen Ausnahmen halten sollte.

Das Problem ist, dass keine der Probleme, die es mit den Alternativen aufzeigt annähernd so schlimm wie die Ausnahme Verhalten sind, die, erinnern wir uns, ist „nicht definiertes Verhalten Ihres Programms“. Einige der Autor Einwände sind „ästhetisch hässlich“ und „schlechten Stil fördern“. Nun was würden Sie lieber? Ein Programm mit schlechten Stil, oder einem, das nicht definiert Verhalten gezeigt?

Ich bin in der Gruppe, die Auffassung, dass die „scoped guard“ Muster in dem destructor werfen in vielen Situationen nützlich ist - vor allem für Unit-Tests. Jedoch bewusst sein, dass in C ++ 11, in einem Anruf in einem destructor Ergebnissen werfen std::terminate seit Destruktoren implizit mit noexcept kommentiert werden.

Andrzej Krzemienski hat einen großen Beitrag zum Thema Destruktoren, die werfen:

Er verweist darauf, dass C ++ 11 einen Mechanismus hat den Standard-noexcept für Destruktoren außer Kraft zu setzen:

  

In C ++ 11 wird ein destructor implizit als noexcept angegeben. Selbst wenn Sie keine Beschreibung hinzufügen und definieren Sie Ihre destructor wie folgt aus:

  class MyType {
        public: ~MyType() { throw Exception(); }            // ...
  };
     

Der Compiler wird immer noch unsichtbar Spezifikation noexcept zu Ihrem destructor hinzuzufügen. Und das bedeutet, dass der Moment Ihres destructor eine Ausnahme auslöst, wird std::terminate genannt werden, auch wenn es keine Doppelausnahmesituation ist. Wenn Sie wirklich entschlossen sind, Ihre Destruktoren zu erlauben, zu werfen, müssen Sie dies explizit angeben; Sie haben drei Möglichkeiten:

     
      
  • Explizit geben Sie Ihren destructor als noexcept(false),
  •   
  • Vererben Ihre Klasse von einem anderen, der bereits sein destructor als noexcept(false) gibt.
  •   
  • Setzen Sie ein nicht-statisches Datenelement in Ihrer Klasse, die bereits sein destructor als noexcept(false) gibt.
  •   

Wenn Sie schließlich in der destructor werfen entscheiden, sollten Sie immer das Risiko einer Doppel Ausnahme bewusst sein, (wirft während der Stapel aufgrund einer Ausnahme: Abroller ist). Dies würde dazu führen, ein Aufruf an std::terminate und es ist selten, was Sie wollen. Um dieses Verhalten zu vermeiden, können Sie einfach überprüfen, ob es bereits eine Ausnahme, bevor sie einen neuen mit dem std::uncaught_exception() werfen.

  

F: Also meine Frage ist - wenn   Werfen von einem Destruktor führt   undefiniertes Verhalten, wie Sie damit umgehen   Fehler, die während eines destructor?

auftreten

A: Es gibt mehrere Möglichkeiten:

  1. Lassen Sie die Ausnahmen aus Ihrem destructor fließen, und zwar unabhängig von dem, was anderswo passiert. Und so beachten Sie dabei (oder sogar ängstlich), dass std :: beenden können folgen.

  2. Lassen Sie niemals Ausnahme aus Ihrem destructor fließen. Kann in ein Protokoll schreiben werden, einig großer roter schlechter Text, wenn Sie können.

  3. mein fave : Wenn std::uncaught_exception false zurückgibt, können Sie Ausnahmen ausfließen. Wenn es wahr zurückgibt, dann zurück in dem Logging-Ansatz fallen.

Aber ist es gut, in d'toren zu werfen?

Ich stimme mit den meisten der über diesem Wurf am besten in destructor vermieden wird, wo es sein kann. Aber manchmal sind Sie am besten weg zu akzeptieren es passieren kann, und behandelt sie gut. Ich würde 3 oben wählen.

Es gibt ein paar seltsamen Fälle, in denen es ist eigentlich einen große Idee von einem Destruktor zu werfen. Wie der „muß überprüfen“ Fehlercode. Dies ist ein Wert, Art, die von einer Funktion zurückgegeben wird. Wenn der Anrufer liest / die enthaltene Fehlercode überprüft, selbst zerstört der zurückgegebene Wert still. Aber , wenn der zurückgegebene Fehlercode durch die Zeit nicht außerhalb des Bereichs gelesen wurden die Rückgabewerte geht, wird es noch einige Ausnahme werfen, von seinem destructor .

Ich folge derzeit der Politik (die so viele sagen), die Klassen nicht aktiv Ausnahmen von ihren Destruktoren werfen sollten, sondern stattdessen ein öffentliches „close“ Verfahren bieten die Operation auszuführen, die fehlschlagen könnte ...

... aber ich Destruktoren für Container-Typ-Klassen glauben, wie ein Vektor, sollten keine Ausnahmen Maske von Klassen geworfen sie enthalten. In diesem Fall verwende ich eigentlich eine „freie / close“ Methode, die sich selbst rekursiv aufruft. Ja, sagte ich rekursiv. Es gibt eine Methode, um diesen Wahnsinn. Ausnahme Ausbreitung beruht auf wobei ein Stapel: Wenn eine einzige Ausnahme auftritt, dann die beiden verbleibenden Destruktoren noch laufen und die anhängige Ausnahme wird, sobald die Routine zurückkehrt ausbreiten, das ist toll. Wenn mehrere Ausnahmen auftreten, dann (je nach Compiler) entweder die erste Ausnahme wird propagieren oder das Programm beenden, was in Ordnung ist. Wenn so viele Ausnahmen vorkommen, dass die Rekursion den Stapel überläuft, dann ist etwas falsch, und jemand geht, darüber zu erfahren, was auch in Ordnung ist. Persönlich irre ich auf der Seite der Fehler eher Sprengung als versteckt, geheim und heimtückisch.

Der Punkt ist, dass der Behälter bleibt neutral, und es ist bis zu den enthaltenen Klassen zu entscheiden, ob sie sich verhalten oder mich schlecht benehmen im Hinblick auf Ausnahmen von ihren Destruktoren werfen.

Martin Ba (oben) ist auf der rechten Seite Track- Sie Architekt anders für RELEASE und COMMIT-Logik.

Für Release:

Sie sollten alle Fehler essen. Sie befreien Speicher, schließen Verbindungen usw. Niemand sonst im System sollte immer wieder diese Dinge sehen, und Sie verteilen zurück Ressourcen an das OS. Wenn es sieht aus wie Sie echte Fehler müssen Handhabung hier seine wahrscheinlich eine Folge der Design-Fehler in Ihrem Objektmodell.

Für Commit:

Dies ist, wo Sie die gleiche Art von RAII Wrapper-Objekte wollen, dass Dinge wie std :: lock_guard für mutexes bieten. Mit denen Sie setzen die Logik in dem dtor gar nicht begehen. Sie haben eine eigene API für sie, dann Wrapper-Objekte, die sie in ihren dtors begehen Raii und die Fehler dort behandeln. Denken Sie daran, Sie Ausnahmen in einem destructor CATCH kann ganz gut; seine sie das ist tödlich Ausgabe. Auf diese Weise können Sie auch Politik und verschiedene Fehlerbehandlung implementieren nur durch einen anderen Wrapper Aufbau (zB std :: unique_lock vs. std :: lock_guard) und stellt sicher, Sie werden nicht zu nennen vergessen die Logik- begehen, die die einzige halbwegs ist anständige Begründung dafür in einem dtor in dem 1. Platz setzen.

Stellen Sie ein Alarmereignis. Typischerweise sind Alarmereignisse besser Form Ausfall der Benachrichtigung während Objekte Reinigung

Im Gegensatz zu Konstrukteuren, wo werfen Ausnahmen ein sinnvoller Weg sein können, dass die Objekterstellung, um anzuzeigen, gelang es, Ausnahmen sollten nicht in Destruktoren geworfen werden.

Das Problem tritt auf, wenn eine Ausnahme von einem Destruktor während der Stapel Abwickelprozesses geworfen wird. Wenn das geschieht, wird der Compiler in eine Situation bringen, wo er nicht weiß, ob der Stapel Abwickelprozesses fortzusetzen oder die neue Ausnahme zu behandeln. Das Endergebnis ist, dass Ihr Programm wird sofort beendet werden.

Folglich ist die beste Vorgehensweise nur ganz von der Verwendung von Ausnahmen in Destruktoren zu verzichten. Schreibe eine Nachricht an eine Protokolldatei statt.

  

Also meine Frage ist - wenn von einem destructor Ergebnisse werfen in   undefiniertes Verhalten, wie gehen Sie Fehler, die während einem auftreten   destructor?

Das Hauptproblem ist dies: Sie können nicht nicht scheitern . Was bedeutet es, scheitern zu scheitern, nachdem alle? Wenn begeht nicht eine Transaktion an eine Datenbank, und es nicht scheitern (nicht rückgängig zu machen), was auf die Integrität unserer Daten passiert?

Da Destruktoren für sowohl normale als auch außergewöhnliche aufgerufen werden (gescheitert) Pfade, sie sich nicht versagen können oder sonst sind wir „failing to fail“.

Dies ist ein konzeptionell schwieriges Problem, aber oft ist die Lösung nur einen Weg finden, um sicherzustellen, dass andernfalls kann nicht umhin. Zum Beispiel könnte eine Datenbankänderung schreibt vor an einer externen Datenstruktur oder Datei zu begehen. Wenn die Transaktion fehlschlägt, dann kann die Datei / Datenstruktur weg geworfen werden. Alles, was sie gewährleisten muss, dass dann die Änderungen von dieser äußeren Struktur / Datei eine atomare Transaktion zu begehen, die nicht fehlschlagen können.

  

Die pragmatische Lösung ist vielleicht nur sicherstellen, dass die Chancen für   Fehler bei Ausfall sind astronomisch unwahrscheinlich, da die Dinge machen   unmöglich zu scheitern fast unmöglich, in einigen Fällen zum Scheitern verurteilt sein.

Die richtige Lösung für mich ist Ihre nicht-Bereinigung Logik in einer Weise, so zu schreiben, dass die Logik Bereinigung kann nicht fehlschlagen. Zum Beispiel, wenn Sie eine neue Datenstruktur, um eine vorhandene Datenstruktur zu bereinigen, zu schaffen, sind versucht, dann vielleicht könnte versuchen, Ihnen die Hilfsstruktur im Voraus zu schaffen, so dass wir sie nicht mehr in einem destructor erstellen müssen.

Das ist alles viel leichter gesagt als getan, zugegeben, aber es ist die einzige wirklich richtige Art und Weise, das ich über sie gehen zu sehen. Manchmal denke ich, es sollte eine Möglichkeit sein, um getrennte destructor Logik für die normale Ausführungspfade zu schreiben weg von aussergewöhnlichen, da manchmal Destruktoren ein wenig das Gefühl, sie haben die doppelte Verantwortung, indem Sie versuchen beide zu behandeln (ein Beispiel ist Wachen Umfang die explizite Kündigung verlangen ;., sie würden dies nicht verlangen, wenn sie außergewöhnliche Zerstörung Wege von nicht-aussergewöhnlichen) unterscheiden können

Immer noch das letzte Problem ist, dass wir nicht scheitern scheitern können, und es ist ein harte Konzeptionierung Problem perfekt in allen Fällen zu lösen. Es geht einfacher, wenn Sie nicht sehr in komplexen Kontrollstrukturen mit Tonnen von Teeny Objekten verpackt miteinander interagieren, und stattdessen Ihre Entwürfe in einer etwas sperrigen Art und Weise (zB Modell: Partikelsystem mit einem destructor des gesamte Teilchen zu zerstören System, kein separater nicht-trivialer destructor pro Teilchen). Wenn Sie Ihre Entwürfe in dieser Art von gröberen Ebene modellieren, haben Sie weniger nicht-triviale Destruktoren zu behandeln, und auch oft kann es sich leisten, was Speicher / Verarbeitungsaufwand erforderlich ist, um sicherzustellen, dass Ihre Destruktoren können nicht versagen.

Und das ist eine der einfachsten Lösungen ist natürlich Destruktoren verwenden weniger häufig. Im Partikel obigen Beispiel vielleicht auf die Zerstörung / ein Teilchen zu entfernen, sollten einige Dinge getan werden, dass aus irgendeinem Grund ausfallen könnten. 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 entfernt ein Teilchen. Entfernen eines Teilchens könnte immer während einer nicht-außergewöhnlichen Weg erfolgen. Wenn das System zerstört, vielleicht kann es nur alle Partikel reinigen und nicht mit dieser einzelnen Partikelentfernung Logik stören, die ausfallen können, während die Logik, die nur scheitern kann während des Partikelsystems der normalen Ausführung ausgeführt wird, wenn es das Entfernen von einem oder mehreren Teilchen.

Es gibt oft Lösungen wie das, was auftauchen, wenn Sie mit vielen Teeny Objekten mit nicht-trivialen Destruktoren Umgang vermeiden. Wo kann man in einem Chaos verheddern, wo es fast unmöglich scheint Ausnahme-Sicherheit zu seinist, wenn Sie in vielen Teeny Objekten, die alle nicht-triviale dtors tun verheddern up.

Es würde viel helfen, wenn nothrow / noexcept tatsächlich in einen Compiler-Fehler übersetzt, wenn alles, was es gibt (virtuelle Funktionen enthält, die die noexcept Spezifikation seiner Basisklasse erben sollte) versucht, etwas zu berufen, die werfen könnte. Auf diese Weise würden wir in der Lage, all dieses Zeug zur Compile-Zeit zu fangen, wenn wir tatsächlich eine destructor versehentlich schreiben, die werfen könnte.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top