werfen Sie eine neue std :: Ausnahme gegen werfen Sie eine std :: Ausnahme

113

Beim Betrachten eines Codes bin ich auf Folgendes gestoßen:

throw /*-->*/new std::exception ("//...

und ich dachte immer, dass du hier nicht brauchst / nicht verwenden solltest new.
Was ist der richtige Weg, sind beide in Ordnung, wenn ja, gibt es einen Unterschied?

Übrigens, was ich beim "Greifen" mit PowerShell-Boost-Bibliotheken sehen kann, wird nie verwendet throw new.

PS Ich habe auch einen CLI-Code gefunden, der verwendet throw gcnew. Ist das in Ordnung?

NoSenseEtAl
quelle
1
Ich denke throw gcnewwäre nützlich zB. Wenn Sie möchten, dass verwalteter Code Ihre Ausnahme abfängt. Kann mich jemand korrigieren?
Jpalecek
1
.Net behandelt Ausnahmen per Zeiger, daher ist es das Richtige, gcnew zu werfen.
Sebastian Redl
1
@SebastianRedl .Net "Zeiger" kann mehrdeutig sein? Obwohl gcnew sicherlich nicht ist. System::Exceptionist im Allgemeinen ein Verweis auf ein verwaltetes Objekt auf dem Garbage Collected Heap. Ich habe immer mit geworfen gcnewund gefangen mit System::Exception ^. Natürlich verwende ich die finallyganze Zeit auch in C ++ / CLI, obwohl ich nicht oft mit C ++ - Ausnahmen im selben tryBlock mische , bin ich mir nicht sicher warum.

Antworten:

89

Die herkömmliche Methode zum Auslösen und Abfangen von Ausnahmen besteht darin, ein Ausnahmeobjekt auszulösen und es als Referenz (normalerweise als constReferenz) abzufangen. Die C ++ - Sprache erfordert, dass der Compiler den entsprechenden Code generiert, um das Ausnahmeobjekt zu erstellen und es zum richtigen Zeitpunkt ordnungsgemäß zu bereinigen.

Es ist niemals eine gute Idee, einen Zeiger auf ein dynamisch zugewiesenes Objekt zu werfen. Ausnahmen sollen es Ihnen ermöglichen, angesichts von Fehlerbedingungen robusteren Code zu schreiben. Wenn Sie ein Ausnahmeobjekt auf herkömmliche Weise auslösen, können Sie sicher sein, dass es zum richtigen catch (...)Zeitpunkt korrekt zerstört wird , wenn es von einer catch-Klausel mit dem Namen des richtigen Typs abgefangen wird , ob es dann erneut ausgelöst wird oder nicht. (Die einzige Ausnahme ist, wenn es nie gefangen wird, aber dies ist eine nicht behebbare Situation, wie auch immer Sie es betrachten.)

Wenn Sie einen Zeiger auf ein dynamisch zugewiesenes Objekt werfen, müssen Sie sicherstellen, dass unabhängig davon, wie der Aufrufstapel an dem Punkt aussieht, an dem Sie Ihre Ausnahme auslösen möchten, ein catch-Block vorhanden ist, der den richtigen Zeigertyp benennt und den entsprechenden deleteAufruf hat. Ihre Ausnahme darf niemals abgefangen werden, es catch (...)sei denn, dieser Block löst die Ausnahme erneut aus, die dann von einem anderen catch-Block abgefangen wird, der die Ausnahme korrekt behandelt.

Tatsächlich bedeutet dies, dass Sie die Ausnahmebehandlungsfunktion verwendet haben, die das Schreiben von robustem Code erleichtern und es sehr schwierig machen soll, Code zu schreiben, der in allen Situationen korrekt ist. Abgesehen von dem Problem, dass es fast unmöglich sein wird, als Bibliothekscode für Client-Code zu fungieren, der diese Funktion nicht erwartet.

CB Bailey
quelle
1
"ein Ausnahmeobjekt werfen" Stapel oder Haufen mein Freund? Stapel oder Haufen? (Vielleicht habe ich mir irgendwo ein schlechtes globales Beispiel angesehen.) Oh, und wenn Stapel, was ist dann der geeignete Bereich?
@ebyrob: Ich bin mir nicht sicher, wonach Sie fragen, aber es hört sich so an, als ob Sie etwas über den Speicher und / oder die Lebensdauer des Ausnahmeobjekts wissen möchten, das hier beantwortet werden könnte . Wenn nicht, ist es möglicherweise besser, eine separate Frage zu stellen.
CB Bailey
31

Keine Notwendigkeit newbeim Auslösen einer Ausnahme.

Einfach schreiben:

throw yourexception(yourmessage);

und fangen als:

catch(yourexception const & e)
{
      //your code (probably logging related code)
}

Beachten Sie, dass yourexceptionvon std::exceptiondirekt oder indirekt abgeleitet werden sollte.

Nawaz
quelle
7
Warum? warum nicht verwenden new? warum ableiten yourexceptionvon std::exception?
Walter
Wenn ich faul bin (was zu oft der Fall ist), warum funktioniert das nicht throw std::exception;? g ++ scheint es nicht zu kompilieren ...
7
@ebyrob: std::exceptionist ein Typ, und Sie können keinen Typ werfen , Sie müssen ein Objekt werfen . Die Syntax sollte also so lauten: throw std::exception();Das wird kompiliert. Wie gut das ist, ist eine ganz andere Frage.
Nawaz
22

Das Werfen new std::exceptionist korrekt, wenn die Anrufstelle erwartet, a zu fangen std::exception*. Aber niemand wird damit rechnen, einen Zeiger auf eine Ausnahme zu finden. Selbst wenn Sie dokumentieren, was Ihre Funktion tut, und die Leute die Dokumentation lesen, können sie dennoch vergessen und versuchen, std::exceptionstattdessen einen Verweis auf ein Objekt zu finden.


quelle
27
Das Auslösen new std::exceptionist nur dann korrekt, wenn die aufrufende Site erwartet, einen Zeiger abzufangen UND die Verwaltung der Zuweisungsausnahme zu übernehmen UND es niemals Fälle geben wird, in denen Ihre Funktion von etwas aufgerufen wird, das nicht explizit abfängt den richtigen Zeiger ( catch(...)oder überhaupt keine Behandlung), sonst tritt ein Objektleck auf. Kurz gesagt, dies kann als "nie" angenähert werden.
CB Bailey
Es ist merkwürdig, wie diese Antwort akzeptiert wurde, wenn es wirklich der Kommentar von @ CharlesBailey ist, der die richtige Antwort ist.
John Dibling
@ John: Das ist mir auch in den Sinn gekommen. Aber ich denke, der Doppelsieg hat eine gute Wirkung, wenn ich die trockene Zusammenfassung gebe, und Charles erweitert amüsant die verschiedenen Arten, wie Menschen vergessen können, richtig damit umzugehen. Schade, dass Sie keinen Ruf durch hochgestimmte Kommentare bekommen.
Charles hat seine Antwort nicht gegeben, und dieses A (im Gegensatz zu dem anderen) enthält Erklärungen sowohl im A als auch im Kommentar.
NoSenseEtAl
9

Die C ++ FAQ hat eine nette Diskussion dazu:

  1. https://isocpp.org/wiki/faq/exceptions#what-to-catch
  2. https://isocpp.org/wiki/faq/exceptions#catch-by-ptr-in-mfc

Grundsätzlich gilt: "Es sei denn, es gibt einen guten Grund, nicht nach Referenz zu fangen. Vermeiden Sie es, nach Wert zu fangen, da dadurch eine Kopie erstellt wird und die Kopie ein anderes Verhalten aufweisen kann als das, was geworfen wurde. Nur unter ganz besonderen Umständen sollten Sie mit dem Zeiger fangen." ""

user1202136
quelle
2
Wie immer sind die FAQ schlecht formuliert. Sie können nach Wert oder Referenz fangen. Ein Zeiger ist zufällig ein Wert (den Sie anhand eines Werts oder einer Referenz abfangen). Denken Sie daran, dass sich der Typ Avom Typ unterscheidet. A*Wenn ich das tue, throw A()kann ich NICHT damit fangen, catch(A* e)da es sich um einen völlig anderen Typ handelt.
Martin York
Diese Links sind jetzt defekt.
Stephenspann
1
Ich habe die Links @spanndemic
user1202136
1

Operator new kann nicht garantieren, dass niemals eine Ausnahme ausgelöst wird. Aus diesem Grund würde die Verwendung zum Auslösen einer "gültigen" (beabsichtigten) Ausnahme einen Code erzeugen, bei dem nicht garantiert werden kann, dass er nicht abstürzt. Da es möglicherweise immer nur eine Ausnahme gibt und Ihr Programm versucht, zwei auszulösen, bevor eine davon abgefangen werden kann, kann eine Implementierung Ihr Programm am besten sofort abbrechen, z. B. durch Aufrufen von std :: terminate.

zkoza
quelle