Die Ausnahmebehandlung in C ++ ist auf try / throw / catch beschränkt. Im Gegensatz zu Object Pascal, Java, C # und Python wurde das finally
Konstrukt auch in C ++ 11 nicht implementiert.
Ich habe sehr viel C ++ - Literatur gesehen, die sich mit "Exception Safe Code" befasst. Lippman schreibt, dass sicherer Code für Ausnahmen ein wichtiges, aber fortgeschrittenes, schwieriges Thema ist, das über den Rahmen seines Primers hinausgeht - was zu bedeuten scheint, dass sicherer Code für C ++ nicht grundlegend ist. Herb Sutter widmet dem Thema in seinem Exceptional C ++!
Dennoch scheint es mir, dass viele der Probleme, die beim Versuch aufgetreten sind, "ausnahmesicheren Code" zu schreiben, ziemlich gut gelöst werden könnten, wenn das finally
Konstrukt implementiert würde, so dass der Programmierer sicherstellen kann, dass das Programm selbst im Falle einer Ausnahme wiederhergestellt werden kann in einen sicheren, stabilen und leckagefreien Zustand, in der Nähe der Ressourcenzuweisung und des potenziell problematischen Codes. Als sehr erfahrener Delphi- und C # -Programmierer verwende ich try .. finally-Blöcke ziemlich ausführlich in meinem Code, wie es die meisten Programmierer in diesen Sprachen tun.
Angesichts all der in C ++ 11 implementierten "Schnickschnack" war ich erstaunt, dass "endlich" immer noch nicht da war.
Warum wurde das finally
Konstrukt nie in C ++ implementiert? Es ist wirklich kein sehr schwieriges oder fortgeschrittenes Konzept und trägt wesentlich dazu bei, dem Programmierer das Schreiben von "ausnahmesicherem Code" zu erleichtern.
quelle
finally
in C ++ kein und welche Techniken für die Ausnahmebehandlung werden an seiner Stelle verwendet?" ist gültig und thematisch für diese Seite. Die vorhandenen Antworten decken dies gut ab, denke ich. In eine Diskussion verwandeln über "Sind die Gründe der C ++ - Designer, nicht einzubeziehen,finally
lohnenswert?" und "Solltefinally
zu C ++ hinzugefügt werden?" und die Diskussion über Kommentare zu der Frage und jeder Antwort fortzusetzen, passt nicht zum Modell dieser Q & A-Site.Antworten:
Als zusätzlichen Kommentar zu @ Nemanjas Antwort (die, da sie Stroustrup zitiert, wirklich so gut ist, wie Sie sie erhalten können):
Es geht wirklich nur darum, die Philosophie und Redewendungen von C ++ zu verstehen. Nehmen Sie ein Beispiel für eine Operation, die eine Datenbankverbindung für eine persistente Klasse öffnet und sicherstellen muss, dass diese Verbindung geschlossen wird, wenn eine Ausnahme ausgelöst wird. Dies ist eine Frage der Ausnahmesicherheit und gilt für alle Sprachen mit Ausnahmen (C ++, C #, Delphi ...).
In einer Sprache, die
try
/ verwendetfinally
, könnte der Code ungefähr so aussehen:Einfach und unkompliziert. Es gibt jedoch einige Nachteile:
finally
Block schreiben , sonst lecke ich Ressourcen.DoRiskyOperation
es sich um mehr als einen einzelnen Methodenaufruf handelt - wenn ich imtry
Block etwas zu verarbeiten habe -, ist dieClose
Operation möglicherweise ein gutes Stück von derOpen
Operation entfernt. Ich kann meine Bereinigung nicht direkt neben meiner Anschaffung schreiben.try
/finally
Blöcken haben.Der C ++ - Ansatz würde folgendermaßen aussehen:
Dies löst alle Nachteile des
finally
Ansatzes vollständig . Es hat ein paar eigene Nachteile, aber sie sind relativ gering:ScopedDatabaseConnection
Klasse selbst schreiben müssen . Es ist jedoch eine sehr einfache Implementierung - nur 4 oder 5 Codezeilen.In Anbetracht dieser Vor- und Nachteile empfinde ich RAII als eine viel bessere Technik als RAII
finally
. Ihr Kilometerstand kann variieren.Schließlich
Scoped...
gibt es Bibliotheken wie ScopeGuard und Boost.ScopeExit , die diese Art der deterministischen Bereinigung ermöglichen , da RAII in C ++ eine so gut etablierte Redewendung ist und die Entwickler von der Last des Schreibens zahlreicher Klassen entlastet .quelle
using
Anweisung, mit der Objekte, die dieIDisposable
Schnittstelle implementieren, automatisch bereinigt werden. Während es also möglich ist, etwas falsch zu machen, ist es ziemlich einfach, es richtig zu machen.try/finally
Konstrukt implementiert wird, da der Compiler ein Konstrukt nicht verfügbar machttry/finally
und der Zugriff nur über die klassenbasierte Methode möglich ist Designsprache, ist kein "Vorteil"; Es ist die eigentliche Definition einer Abstraktionsinversion.finally
. Wie gesagt, Ihre Laufleistung kann variieren.Von Warum C nicht ein „endlich“ konstruieren ++ zur Verfügung stellen? in Bjarne Stroustrups C ++ Style und Technik FAQ :
quelle
finally
Konstrukt für immer und ewig unbrauchbar ist, ungeachtet dessen , was Strousup sagt. Die bloße Tatsache, dass das Schreiben von "Exception Safe Code" in C ++ eine große Rolle spielt, ist ein Beweis dafür. Heck, C # hat beide Destruktoren undfinally
, und sie werden beide verwendet.Der Grund, den C ++ nicht hat,
finally
ist, dass es in C ++ nicht benötigt wird.finally
wird verwendet, um Code auszuführen, unabhängig davon, ob eine Ausnahme aufgetreten ist oder nicht. Dies ist fast immer eine Art Bereinigungscode. In C ++ sollte sich dieser Bereinigungscode im Destruktor der betreffenden Klasse befinden, und der Destruktor wird immer wie einfinally
Block aufgerufen . Die Redewendung, den Destruktor für die Bereinigung zu verwenden, heißt RAII .In der C ++ - Community wird möglicherweise mehr über ausnahmesicheren Code gesprochen, in anderen Sprachen mit Ausnahmen ist dies jedoch fast genauso wichtig. Der springende Punkt bei "ausnahmesicherem" Code ist, dass Sie sich überlegen, in welchem Zustand Ihr Code verbleibt, wenn bei einer der von Ihnen aufgerufenen Funktionen / Methoden eine Ausnahme auftritt.
In C ++ ist ausnahmesicherer Code etwas wichtiger, da in C ++ keine automatische Speicherbereinigung vorhanden ist, die Objekte berücksichtigt, die aufgrund einer Ausnahme verwaist sind.
Der Grund, warum die Ausnahmesicherheit in der C ++ - Community mehr diskutiert wird, beruht wahrscheinlich auch auf der Tatsache, dass Sie in C ++ besser wissen müssen, was schief gehen kann, da die Sprache weniger Standardsicherheitsnetze enthält.
quelle
finally
des C ++ - Standards gegeben hat, kann ich mit Sicherheit den Schluss ziehen, dass die C ++ - Community keinthe absence of finally
Problem sieht . Den meisten Sprachenfinally
fehlt die konsistente deterministische Zerstörung von C ++. Ich sehe, dass Delphi beide hat, aber ich kenne die Geschichte nicht gut genug, um zu wissen, welche zuerst da war.finally
". Ich kann mich nie an eine Aufgabe erinnern, die mir leichter gefallen wäre, wenn ich Zugang dazu gehabt hätte.Andere haben RAII als Lösung diskutiert. Es ist eine vollkommen gute Lösung. Aber das spricht nicht wirklich an, warum sie nicht
finally
so gut hinzugefügt haben, da es eine weithin gewünschte Sache ist. Die Antwort darauf ist grundlegender für das Design und die Entwicklung von C ++: Während der gesamten Entwicklung von C ++ haben sich die Beteiligten nachdrücklich gegen die Einführung von Designmerkmalen gewehrt, die mit anderen Features ohne großen Aufwand erreicht werden können, und insbesondere dort, wo dies die Einführung erfordert von neuen Schlüsselwörtern, die älteren Code inkompatibel machen könnten. Da RAII eine hochfunktionale Alternative zu C ++ 11 bietetfinally
und Sie Ihre eigenenfinally
sowieso in C ++ 11 rollen können, gab es wenig Nachfrage danach.Sie müssen lediglich eine Klasse erstellen
Finally
, die die Funktion aufruft, die in ihrem Destruktor an ihren Konstruktor übergeben wird. Dann können Sie dies tun:Die meisten nativen C ++ - Programmierer bevorzugen jedoch im Allgemeinen sauber gestaltete RAII-Objekte.
quelle
Finally atEnd([&] () { database.close(); });
, ich denke, das Folgende ist besser:{ Finally atEnd(...); try {...} catch(e) {...} }
(Ich habe den Finalizer aus dem Try-Block gehoben, damit er nach den Catch-Blöcken ausgeführt wird.)Sie können ein "Trap" -Muster verwenden - auch wenn Sie den try / catch-Block nicht verwenden möchten.
Stellen Sie ein einfaches Objekt in den gewünschten Bereich. In den Destruktor dieses Objekts legen Sie Ihre "endgültige" Logik. Egal was passiert, wenn der Stapel abgewickelt wird, wird der Destruktor des Objekts aufgerufen und Sie erhalten Ihre Süßigkeiten.
quelle
Nun, Sie könnten
finally
mit Lambdas eine Art Roll-Your-Own- Methode verwenden, mit der sich Folgendes gut kompilieren lässt (anhand eines Beispiels ohne RAII, natürlich nicht das schönste Stück Code):Siehe diesen Artikel .
quelle
Ich bin mir nicht sicher, ob ich der Behauptung zustimme, dass RAII eine Obermenge von ist
finally
. Die Achillesferse von RAII ist einfach: Ausnahmen. RAII wird mit Destruktoren implementiert, und es ist in C ++ immer falsch, einen Destruktor zu löschen. Das bedeutet, dass Sie RAII nicht verwenden können, wenn Sie Ihren Bereinigungscode zum Werfen benötigen. Wennfinally
es dagegen implementiert würde, gäbe es keinen Grund zu der Annahme, dass es nicht legal wäre, aus einemfinally
Block zu werfen .Betrachten Sie einen Codepfad wie diesen:
Wenn wir hätten
finally
, könnten wir schreiben:Aber ich kann keine Möglichkeit finden, mit RAII ein gleichwertiges Verhalten zu erzielen.
Wenn jemand weiß, wie man das in C ++ macht, interessiert mich die Antwort sehr. Ich würde mich sogar über etwas freuen, das sich darauf stützt, dass zum Beispiel alle Ausnahmen, die von einer einzelnen Klasse mit einigen besonderen Fähigkeiten geerbt wurden, durchgesetzt werden.
quelle
complex_cleanup
Sie in Ihrem zweiten Beispiel werfen können, könnten Sie einen Fall haben, in dem zwei nicht erfasste Ausnahmen auf einmal im Flug sind, genau wie Sie es mit RAII / destructors tun würden, und C ++ weigert sich, dies zuzulassen. Wenn Sie möchten, dass die ursprüngliche Ausnahme angezeigt wird,complex_cleanup
sollten Sie Ausnahmen verhindern, genau wie bei RAII / destructors. Wenn Sie möchtencomplex_cleanup
, dass die Ausnahme angezeigt wird, können Sie verschachtelte try / catch-Blöcke verwenden. Dies ist jedoch eine Tangente, die sich nur schwer in einen Kommentar einfügen lässt. Daher ist es eine separate Frage wert.finally
Block würde genau so funktionieren wie ein Wurf in einencatch
Block. WRT-Ausnahmen während des Flugs - kein Aufrufstd::terminate
. Die Frage ist "warum nichtfinally
in C ++?" und die Antworten sagen alle: "Du brauchst es nicht ... RAII FTW!" Mein Punkt ist, dass RAII für einfache Fälle wie die Speicherverwaltung in Ordnung ist, aber bis das Problem der Ausnahmen gelöst ist, sind zu viele Überlegungen / Overheads / Bedenken / Neugestaltungen erforderlich, um eine Allzwecklösung zu sein.