In C ++ können Sie mithilfe eines Ausnahmespezifizierers angeben, dass eine Funktion eine Ausnahme auslösen kann oder nicht. Beispielsweise:
void foo() throw(); // guaranteed not to throw an exception
void bar() throw(int); // may throw an exception of type int
void baz() throw(...); // may throw an exception of some unspecified type
Ich bin aus folgenden Gründen zweifelhaft, ob ich sie tatsächlich verwenden soll:
- Der Compiler erzwingt Ausnahmespezifizierer nicht wirklich auf strenge Weise, daher sind die Vorteile nicht groß. Idealerweise möchten Sie einen Kompilierungsfehler erhalten.
- Wenn eine Funktion einen Ausnahmespezifizierer verletzt, besteht das Standardverhalten darin, das Programm zu beenden.
- In VS.Net wird throw (X) als throw (...) behandelt, sodass die Einhaltung des Standards nicht stark ist.
Denken Sie, dass Ausnahmespezifizierer verwendet werden sollten?
Bitte antworten Sie mit "Ja" oder "Nein" und begründen Sie Ihre Antwort.
Antworten:
Nein.
Hier sind einige Beispiele warum:
Vorlagencode kann mit Ausnahmespezifikationen nicht geschrieben werden.
Die Kopien werden möglicherweise ausgelöst, die Parameterübergabe wird möglicherweise ausgelöst und
x()
es wird eine unbekannte Ausnahme ausgelöst.Ausnahmespezifikationen verbieten tendenziell die Erweiterbarkeit.
könnte sich entwickeln in
Das könnte man wirklich so schreiben
Das erste ist nicht erweiterbar, das zweite ist überambitioniert und das dritte ist wirklich das, was Sie meinen, wenn Sie virtuelle Funktionen schreiben.
Legacy-Code
Wenn Sie Code schreiben, der auf einer anderen Bibliothek basiert, wissen Sie nicht genau, was er tun kann, wenn etwas schrecklich schief geht.
g
wird beendet, wennlib_f()
Würfe. Dies ist (in den meisten Fällen) nicht das, was Sie wirklich wollen.std::terminate()
sollte niemals angerufen werden. Es ist immer besser, die Anwendung mit einer nicht behandelten Ausnahme abstürzen zu lassen, von der Sie einen Stack-Trace abrufen können, als still / gewaltsam zu sterben.Schreiben Sie Code, der häufige Fehler zurückgibt und in Ausnahmefällen auslöst.
Wenn Ihre Bibliothek jedoch nur Ihre eigenen Ausnahmen auslöst, können Sie Ausnahmespezifikationen verwenden, um Ihre Absicht anzugeben.
quelle
try {...} catch (<specified exceptions>) { <do whatever> } catch (...) { unexpected(); ]
Konstrukt eingeschlossen, unabhängig davon, ob Sie dort einen try-Block möchten oder nicht.try { <<...code...>> } catch(...) /* stack guaranteed to be unwound here and dtors run */ { throw; /* pass it on to the runtime */ }
terminate()
? Warum nicht einfach anrufenabort()
?Vermeiden Sie Ausnahmespezifikationen in C ++. Die Gründe, die Sie in Ihrer Frage angeben, sind ein ziemlich guter Anfang für das Warum.
Siehe Herb Sutters "Ein pragmatischer Blick auf Ausnahmespezifikationen" .
quelle
throw(optional-type-id-list)
) ist in C ++ 11 veraltet. Sie sind immer noch im Standard, aber ich denke, es wurde ein Warnschuss gesendet, dass ihre Verwendung sorgfältig abgewogen werden sollte. C ++ 11 fügt dienoexcept
Spezifikation und den Operator hinzu. Ich weiß nicht genug Details, um esnoexcept
zu kommentieren. Dieser Artikel scheint ziemlich detailliert zu sein: akrzemi1.wordpress.com/2011/06/10/using-noexcept Und Dietmar Kühl hat einen Artikel im Juni 2011 Overload Journal: accu.org/var/uploads/journals/overload103.pdfthrow(something)
gilt als nutzlos und eine schlechte Idee.throw()
ist nützlich.Ich denke, dass die Standardausnahmespezifizierer mit Ausnahme der Konvention (für C ++)
ein Experiment im C ++ - Standard waren, das größtenteils fehlgeschlagen ist.
Die Ausnahme besteht darin, dass der No-Throw-Bezeichner nützlich ist. Sie sollten jedoch auch intern den entsprechenden try catch-Block hinzufügen, um sicherzustellen, dass der Code mit dem Bezeichner übereinstimmt. Herb Sutter hat eine Seite zu diesem Thema. Gotch 82
Darüber hinaus halte ich es für sinnvoll, Ausnahmegarantien zu beschreiben.
Hierbei handelt es sich im Wesentlichen um eine Dokumentation darüber, wie der Status eines Objekts von Ausnahmen beeinflusst wird, die einer Methode für dieses Objekt entgehen. Leider werden sie vom Compiler nicht erzwungen oder anderweitig erwähnt.
Boost und Ausnahmen
Ausnahmegarantien
Keine Garantie:
Grundgarantie:
Starke Garantie: (auch bekannt als Transaktionsgarantie)
Keine Wurfgarantie:
quelle
Guarantee that functions will only throw listed exceptions (possibly none)
. Nicht wahr. Es wird nur garantiert, dass die Anwendung beendet wird, wenn die Funktion diese Ausnahmen auslöst.Enable compiler optimizations based on the knowledge that only listed exceptions (possibly none) will be thrown
Kann das nicht machen. Weil ich gerade darauf hingewiesen habe, können Sie nicht garantieren, dass die Ausnahme nicht ausgelöst wird.throw()
(wirft nicht). " Es wird nur garantiert, dass die Anwendung beendet wird, wenn die Funktion diese Ausnahmen auslöst. " Nein "nicht wahr" bedeutet wahr. In C ++ gibt es keine Garantie dafür, dass eine Funktionterminate()
niemals aufgerufen wird. " Weil ich gerade darauf hingewiesen habe, dass Sie nicht garantieren können, dass die Ausnahme nicht ausgelöst wird. " Sie garantieren, dass die Funktion nicht ausgelöst wird. Welches ist genau das, was Sie brauchen.gcc gibt Warnungen aus, wenn Sie Ausnahmespezifikationen verletzen. Ich verwende Makros, um die Ausnahmespezifikationen nur in einem "Flusen" -Modus zu verwenden, der ausdrücklich zur Überprüfung kompiliert wird, um sicherzustellen, dass die Ausnahmen mit meiner Dokumentation übereinstimmen.
quelle
Der einzige nützliche Ausnahmespezifizierer ist "throw ()", wie in "wirft nicht".
quelle
Ausnahmespezifikationen sind in C ++ keine wunderbar nützlichen Werkzeuge. Es gibt jedoch eine gute Verwendung für sie, wenn sie mit std :: unerwartet kombiniert werden.
In einigen Projekten mache ich Code mit Ausnahmespezifikationen und rufe dann set_unexpected () mit einer Funktion auf, die eine spezielle Ausnahme meines eigenen Designs auslöst. Diese Ausnahme erhält bei der Erstellung eine plattformspezifische Rückverfolgung und wird von std :: bad_exception abgeleitet (damit sie bei Bedarf weitergegeben werden kann). Wenn es wie üblich zu einem terminate () -Aufruf kommt, wird die Rückverfolgung durch what () (sowie die ursprüngliche Ausnahme, die sie verursacht hat; nicht zu schwer zu finden) gedruckt, und ich erhalte Informationen darüber, wo sich mein Vertrag befand verletzt, z. B. welche unerwartete Bibliotheksausnahme ausgelöst wurde.
Wenn ich dies tue, erlaube ich niemals die Weitergabe von Bibliotheksausnahmen (außer Standardausnahmen) und leite alle meine Ausnahmen von std :: exception ab. Wenn sich eine Bibliothek zum Werfen entscheidet, werde ich abfangen und in meine eigene Hierarchie konvertieren, sodass ich den Code immer kontrollieren kann. Vorlagenfunktionen, die abhängige Funktionen aufrufen, sollten aus offensichtlichen Gründen Ausnahmespezifikationen vermeiden. Es ist jedoch sowieso selten, dass eine Vorlagenfunktionsschnittstelle mit Bibliothekscode vorhanden ist (und nur wenige Bibliotheken verwenden Vorlagen wirklich auf nützliche Weise).
quelle
Wenn Sie Code schreiben, der von Personen verwendet wird, die sich lieber die Funktionsdeklaration als Kommentare dazu ansehen, gibt eine Spezifikation an, welche Ausnahmen sie möglicherweise abfangen möchten.
Ansonsten finde ich es nicht besonders nützlich, etwas anderes zu verwenden, als
throw()
anzuzeigen, dass es keine Ausnahmen auslöst.quelle
Nein. Wenn Sie sie verwenden und eine Ausnahme ausgelöst wird, die Sie weder durch Ihren Code noch durch Ihren Code angegeben haben, besteht das Standardverhalten darin, Ihr Programm sofort zu beenden.
Ich glaube auch, dass ihre Verwendung in aktuellen Entwürfen des C ++ 0x-Standards veraltet ist.
quelle
Eine "throw ()" - Spezifikation ermöglicht es dem Compiler, einige Optimierungen bei der Codeflussanalyse durchzuführen, wenn er weiß, dass die Funktion niemals eine Ausnahme auslöst (oder zumindest verspricht, niemals eine Ausnahme auszulösen). Larry Osterman spricht hier kurz darüber:
http://blogs.msdn.com/larryosterman/archive/2006/03/22/558390.aspx
quelle
Im Allgemeinen würde ich keine Ausnahmespezifizierer verwenden. In Fällen, in denen eine andere Ausnahme von der fraglichen Funktion kommen sollte, die das Programm definitiv nicht korrigieren kann , kann dies nützlich sein. Stellen Sie in jedem Fall sicher, dass Sie klar dokumentieren, welche Ausnahmen von dieser Funktion zu erwarten sind.
Ja, das erwartete Verhalten einer nicht angegebenen Ausnahme, die von einer Funktion mit Ausnahmespezifizierern ausgelöst wird, besteht darin, terminate () aufzurufen.
Ich werde auch bemerken, dass Scott Meyers dieses Thema in More Effective C ++ anspricht. Sein Effective C ++ und More Effective C ++ sind sehr empfehlenswerte Bücher.
quelle
Ja, wenn Sie sich für interne Dokumentation interessieren. Oder schreiben Sie eine Bibliothek, die andere verwenden, damit sie erkennen können, was passiert, ohne die Dokumentation zu konsultieren. Werfen oder nicht werfen kann als Teil der API betrachtet werden, fast wie der Rückgabewert.
Ich stimme zu, sie sind nicht wirklich nützlich, um die Korrektheit des Java-Stils im Compiler durchzusetzen, aber es ist besser als nichts oder zufällige Kommentare.
quelle
Sie können für Komponententests nützlich sein, damit Sie beim Schreiben von Tests wissen, was die Funktion erwartet, wenn sie fehlschlägt, aber im Compiler gibt es keine Durchsetzung, die sie umgibt. Ich denke, dass es sich um zusätzlichen Code handelt, der in C ++ nicht erforderlich ist. Was auch immer Sie auswählen, alles, worauf Sie sich verlassen sollten, ist, dass Sie im gesamten Projekt und in den Teammitgliedern denselben Codierungsstandard befolgen, damit Ihr Code lesbar bleibt.
quelle
Aus dem Artikel:
http://www.boost.org/community/exception_safety.html
Und tatsächlich kann ich mir Möglichkeiten vorstellen, um Vorlagenklassen ausnahmesicher zu machen. Wenn Sie nicht die Kontrolle über alle Unterklassen haben, haben Sie möglicherweise trotzdem ein Problem. Zu diesem Zweck können Sie in Ihren Klassen Typedefs erstellen, die die von verschiedenen Vorlagenklassen ausgelösten Ausnahmen definieren. Ich denke, das Problem besteht darin, es wie immer danach anzugehen, anstatt es von Anfang an zu entwerfen, und ich denke, es ist dieser Aufwand, der die wahre Hürde darstellt.
quelle
Ausnahmespezifikationen = Müll, fragen Sie jeden Java-Entwickler über 30 Jahre
quelle