Ich stimme zwar zu, dass das Fangen ...
ohne erneutes Werfen in der Tat falsch ist, aber ich glaube, dass die Verwendung von Konstrukten wie folgt :
try
{
// Stuff
}
catch (...)
{
// Some cleanup
throw;
}
Ist in Fällen akzeptabel, in denen RAII nicht anwendbar ist . (Bitte fragen Sie nicht ... nicht jeder in meiner Firma mag objektorientiertes Programmieren und RAII wird oft als "nutzloses Schulmaterial" angesehen ...)
Meine Kollegen sagen, dass Sie immer wissen sollten, welche Ausnahmen geworfen werden sollen und dass Sie immer Konstrukte verwenden können wie:
try
{
// Stuff
}
catch (exception_type1&)
{
// Some cleanup
throw;
}
catch (exception_type2&)
{
// Some cleanup
throw;
}
catch (exception_type3&)
{
// Some cleanup
throw;
}
Gibt es eine allgemein anerkannte gute Praxis in Bezug auf diese Situationen?
...
", während sich meine Frage auf "Soll ich besser fangen...
oder<specific exception>
bevor ich erneut wirf" konzentriertmain
,catch(...) { return EXIT_FAILURE; }
könnte auch richtig sein in Code, der nicht unter einem Debugger ausgeführt wird . Wenn Sie nicht fangen, wird der Stapel möglicherweise nicht abgewickelt. Nur wenn Ihr Debugger nicht erfasste Ausnahmen erkennt, möchten Sie, dass sie beendet werdenmain
.Antworten:
Ihr Kollege hat offenbar noch nie an Universalbibliotheken gearbeitet.
Wie in der Welt kann eine Klasse wie
std::vector
auch vorgeben zu wissen , was die Kopierkonstruktoren werfen, während sie noch Ausnahme Sicherheit zu gewährleisten?Wenn Sie immer wüssten, was der Angerufene beim Kompilieren tun würde, wäre Polymorphismus nutzlos! Manchmal besteht das gesamte Ziel darin, das zu abstrahieren, was auf einer niedrigeren Ebene passiert. Sie möchten also nicht genau wissen, was los ist.
quelle
...
.Was Sie gefangen zu sein scheinen, ist die Hölle, in der jemand versucht, seinen Kuchen zu haben und ihn auch zu essen.
RAII und Ausnahmen sollen Hand in Hand gehen. RAII ist das Mittel, mit dem Sie nicht viele Anweisungen schreiben müssen , um
catch(...)
aufzuräumen. Dies geschieht selbstverständlich automatisch. Und Ausnahmen sind die einzige Möglichkeit, mit RAII-Objekten zu arbeiten, da Konstruktoren nur erfolgreich sein oder werfen können (oder das Objekt in einen Fehlerzustand versetzen können, aber wer will das?).Eine
catch
Anweisung kann eine der beiden folgenden Aufgaben ausführen: Fehler oder außergewöhnliche Umstände behandeln oder Aufräumarbeiten ausführen. Manchmal macht es beides, aber es gibt jedecatch
Anweisung, um mindestens eine davon zu tun.catch(...)
kann keine ordnungsgemäße Ausnahmebehandlung durchführen. Sie wissen nicht, was die Ausnahme ist. Sie können keine Informationen über die Ausnahme erhalten. Sie haben absolut keine andere Information als die Tatsache, dass eine Ausnahme von etwas in einem bestimmten Codeblock ausgelöst wurde . Das einzig legitime, was Sie in einem solchen Block tun können, ist aufzuräumen. Und das bedeutet, die Ausnahme am Ende der Bereinigung erneut auszulösen.Was RAII Ihnen in Bezug auf die Ausnahmebehandlung bietet, ist die kostenlose Bereinigung. Wenn alles ordnungsgemäß in RAII eingekapselt ist, wird alles ordnungsgemäß bereinigt.
catch
Anweisungen müssen nicht mehr bereinigt werden. In diesem Fall gibt es keinen Grund, einecatch(...)
Erklärung abzugeben.Also würde ich zustimmen, dass
catch(...)
das meistens böse ist ... vorläufig .Diese Bestimmung ist die ordnungsgemäße Verwendung von RAII. Denn ohne sie, Sie müssen bestimmte Bereinigungen tun zu können. Daran führt kein Weg vorbei. Sie müssen in der Lage sein, Aufräumarbeiten zu erledigen. Sie müssen sicherstellen können, dass durch das Auslösen einer Ausnahme der Code in einem angemessenen Zustand bleibt. Und
catch(...)
ist dabei ein wichtiges Instrument.Sie können nicht eins ohne das andere haben. Man kann nicht sagen, dass sowohl RAII als
catch(...)
auch schlecht sind. Sie benötigen mindestens eines davon. Andernfalls sind Sie nicht ausnahmesicher.Natürlich gibt es eine gültige, wenn auch seltene Verwendung
catch(...)
, die nicht einmal RAII verbannen kann: dieexception_ptr
Weiterleitung an eine andere Person. In der Regel über einepromise/future
oder eine ähnliche Schnittstelle.Ihr Mitarbeiter ist ein Idiot (oder einfach nur schrecklich unwissend). Dies sollte sofort offensichtlich sein, da er vorschlägt, wie viel Code zum Kopieren und Einfügen Sie schreiben. Die Bereinigung für jede dieser catch-Anweisungen ist genau gleich . Das ist ein Wartungs-Albtraum, von der Lesbarkeit ganz zu schweigen.
Kurz gesagt: Dies ist das Problem, für dessen Lösung RAII erstellt wurde (nicht, dass es andere Probleme nicht löst).
Was mich an dieser Vorstellung verwirrt, ist, dass es im Allgemeinen rückständig ist, wie die meisten Leute behaupten, dass RAII schlecht ist. Im Allgemeinen lautet das Argument "RAII ist schlecht, weil Sie Ausnahmen verwenden müssen, um Konstruktorfehler zu signalisieren. Sie können jedoch keine Ausnahmen auslösen, da es nicht sicher ist und Sie viele
catch
Anweisungen benötigen, um alles zu bereinigen." Das ist ein kaputtes Argument, weil RAII das Problem löst , das der Mangel an RAII verursacht.Höchstwahrscheinlich ist er gegen RAII, weil es Details verbirgt. Destruktor-Aufrufe sind bei automatischen Variablen nicht sofort sichtbar. Sie erhalten also Code, der implizit aufgerufen wird. Einige Programmierer hassen das wirklich. Anscheinend
catch
ist es eine bessere Idee , drei Anweisungen zu haben, die alle dasselbe mit Code zum Kopieren und Einfügen tun.quelle
Wirklich zwei Kommentare. Das erste ist, dass Sie in einer idealen Welt immer wissen sollten, welche Ausnahmen in der Praxis möglicherweise ausgelöst werden, wenn Sie mit Bibliotheken von Drittanbietern oder mit einem Microsoft-Compiler arbeiten. Mehr auf den Punkt; Auch wenn Sie alle möglichen Ausnahmen genau kennen, ist das hier relevant?
catch (...)
drückt die Absicht weitaus besser aus alscatch ( std::exception const& )
, selbst wenn alle möglichen Ausnahmen davon herrührenstd::exception
(was in einer idealen Welt der Fall wäre). Was die Verwendung mehrerer Fangblöcke angeht, wenn es keine gemeinsame Grundlage für alle Ausnahmen gibt: Das ist völlige Verschleierung und ein Alptraum für die Instandhaltung. Woran erkennt man, dass alle Verhaltensweisen identisch sind? Und das war die Absicht? Und was passiert, wenn Sie das Verhalten ändern müssen (z. B. Fehlerbehebung)? Es ist allzu leicht, einen zu verpassen.quelle
std::exception
unserer Codebasis abstammt und jeden Tag versucht, ihre Verwendung durchzusetzen. Ich vermute, dass er versucht, mich für die Verwendung von Code und externen Bibliotheken zu bestrafen, die er nicht selbst geschrieben hat.std::vector<>
kann jede Art von Ausnahme aus so ziemlich jedem Grund auslösen.Ich denke, Ihr Kollege hat einige gute Ratschläge vertauscht - Sie sollten bekannte Ausnahmen nur in einem
catch
Block behandeln, wenn Sie sie nicht erneut werfen.Das heisst:
Ist schlecht, weil es jeden Fehler still versteckt .
Jedoch:
Ist in Ordnung - wir wissen, womit wir es zu tun haben und müssen es nicht dem aufrufenden Code aussetzen.
Gleichfalls:
Ist in Ordnung, sogar Best Practice, sollte der Code für allgemeine Fehler mit dem Code übereinstimmen, der sie verursacht. Es ist besser, als sich auf den Angerufenen zu verlassen, um zu wissen, dass eine Transaktion rückgängig gemacht werden muss oder was auch immer.
quelle
Jede Antwort mit Ja oder Nein sollte mit einer Begründung versehen sein, warum dies so ist.
Zu sagen, dass es falsch ist, nur weil mir das beigebracht wurde, ist blinder Fanatismus.
Das gleiche
//Some cleanup; throw
mehrmals zu schreiben , wie in Ihrem Beispiel, ist falsch, da es sich um Codeduplizierung handelt und dies eine Wartungslast darstellt. Es ist besser, es nur einmal zu schreiben.Das Schreiben von a
catch(...)
, um alle Ausnahmen zum Schweigen zu bringen, ist falsch, da Sie nur mit Ausnahmen umgehen sollten, die Sie zu behandeln wissen. Mit diesem Platzhalter können Sie mehr als erwartet kreuzen und wichtige Fehler zum Schweigen bringen.Wenn Sie jedoch nach a erneut werfen
catch(...)
, gilt das letztere nicht mehr, da Sie die Ausnahme nicht tatsächlich behandeln. Es gibt also keinen Grund, warum dies nicht empfohlen wird.Eigentlich habe ich das gemacht, um sensible Funktionen ohne Probleme einzuloggen:
quelle
Log(...)
nicht werfen können.Generell stimme ich der Stimmung der Posts hier zu, ich mag es nicht, bestimmte Ausnahmen zu erfassen - ich denke, die Syntax dafür steckt noch in den Kinderschuhen und kann den redundanten Code noch nicht verarbeiten.
Aber da alle das sagen, werde ich darauf hinweisen, dass ich, obwohl ich sie sparsam benutze, oft eine meiner "catch (Exception e)" - Anweisungen angesehen und gesagt habe: "Verdammt, ich wünschte, ich hätte angerufen "Wenn Sie später eintreten, ist es oft schön zu wissen, was die Absicht war und was der Kunde wahrscheinlich auf einen Blick werfen wird.
Ich rechtfertige die Einstellung von "Immer x verwenden" nicht, sondern sage nur, dass es gelegentlich schön ist, sie aufgelistet zu sehen, und ich bin sicher, dass dies der Grund ist, warum manche Leute meinen, es sei der "richtige" Weg.
quelle