2012-06-08 29 views
86

während irgend Code ich auf der Suche auf gestolpert:throw new std :: exception vs throw std :: exception

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

und ich dachte immer, dass Sie nicht brauchen/Sie nicht new hier verwendet werden soll.
Was ist der richtige Weg, sind beide OK, wenn ja, gibt es einen Unterschied?

BTW von dem, was ich sehen kann, während "grepping" mit PowerShell Boost libs nie throw new verwenden.

P.S. Außerdem habe ich einen CLI-Code gefunden, der throw gcnew verwendet. Ist das in Ordnung?

+0

Ich denke 'throw gcnew' wäre nützlich z. Wenn Sie möchten, dass verwalteter Code Ihre Ausnahme abfängt. Kann mich das jemand korrigieren? – jpalecek

+1

.Net befasst sich mit Ausnahmen durch Zeiger, so dass Throw gcnew das Richtige ist, um dort zu tun. –

+1

@SebastianRedl .Net "Zeiger" kann mehrdeutig sein? Obwohl GCNEW sicherlich nicht ist. "System :: Exception" ist im Allgemeinen ein Verweis auf ein verwaltetes Objekt auf dem Garbage Collected Heap. Ich habe immer mit 'gcnew' geworfen und mit' System :: Exception^'abgefangen. Natürlich benutze ich 'finally' die ganze Zeit auch in C++/CLI, obwohl ich nicht oft mit C++ Ausnahmen im selben' try' Block mische, ich bin mir nicht sicher warum. –

Antwort

72

Der herkömmliche Weg zum Auslösen und Abfangen von Ausnahmen besteht darin, ein Ausnahmeobjekt zu werfen und es als Referenz abzufangen (normalerweise const Referenz). Die Sprache C++ erfordert, dass der Compiler den entsprechenden Code generiert, um das Ausnahmeobjekt zu konstruieren und es zu gegebener Zeit ordnungsgemäß zu bereinigen.

Einen Zeiger auf ein dynamisch zugewiesenes Objekt zu werfen ist nie eine gute Idee. Ausnahmen sollen Ihnen ermöglichen, robusteren Code angesichts von Fehlerbedingungen zu schreiben. Wenn Sie ein Exception-Objekt auf die herkömmliche Art und Weise werfen, können Sie sicher sein, ob es durch eine catch-Klausel, die den korrekten Typ nennt, von einer catch (...) abgefangen wird, ob es dann erneut geworfen wird oder nicht, es wird zur richtigen Zeit korrekt zerstört . (Die einzige Ausnahme ist, wenn sie überhaupt nicht abgefangen wird, aber dies ist eine nicht behebbare Situation, egal wie man sie betrachtet.)

Wenn Sie einen Zeiger auf ein dynamisch zugewiesenes Objekt werfen, müssen Sie sicher sein, dass was auch immer Call-Stack sieht aus wie an dem Punkt, an dem Sie Ihre Ausnahme auslösen möchten, gibt es einen catch-Block, der den richtigen Zeigertyp nennt und den entsprechenden Aufruf delete hat.Ihre Exception darf niemals von abgefangen werden, es sei denn, dieser Block löst die Ausnahme erneut aus, die dann von einem anderen catch-Block abgefangen wird, der die Ausnahme korrekt behandelt.

Effektiv bedeutet dies, dass Sie die Funktion zur Ausnahmebehandlung verwendet haben, die es einfacher macht, robusten Code zu schreiben, und es sehr schwer macht, in allen Situationen korrekten Code zu schreiben. Dies schließt das Problem aus, dass es fast unmöglich ist, als Bibliothekscode für Client-Code zu fungieren, der diese Funktion nicht erwartet.

+1

"ein Ausnahmeobjekt werfen" Stapel oder Heap mein Freund? Stapel oder Haufen? (Vielleicht habe ich irgendwo ein schlechtes globales Beispiel betrachtet) Oh, und wenn Stack, was ist dann der richtige Bereich? –

+0

@ebyrob: Ich bin mir nicht sicher, wonach du fragst, aber es klingt, als ob du etwas über den Speicher und/oder die Lebensdauer des Ausnahmeobjekts wissen möchtest, das hier beantwortet werden kann (http://stackoverflow.com)/questions/1654150/scope-of-exception-objekt-in-c). Wenn nicht, sollten Sie besser eine separate Frage stellen. –

26

Keine Notwendigkeit, new zu verwenden, wenn Ausnahme ausgelöst wird.

Schreiben Sie einfach:

throw yourexception(yourmessage); 

und fangen, wie:

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

Beachten Sie, dass yourexception von std::exception direkt oder indirekt ableiten sollte.

+5

Warum? Warum nicht "neu" benutzen? Warum leiten Sie 'yourexception' von' std :: exception' ab? – Walter

+0

Wenn ich faul bin (was waaay zu oft ist), warum wirft 'std :: exception;' nicht? g ++ scheint es nicht zu kompilieren ... –

+5

@ebyrob: 'std :: exception' ist ein Typ, und du kannst keinen * type * werfen, du musst ein * Objekt * werfen. Die Syntax sollte also lauten: 'throw std :: exception();' Das wird kompiliert. Wie gut das ist, ist eine ganz andere Frage. – Nawaz

21

Das Werfen new std::exception ist korrekt, wenn die Aufruf-Site erwartet, eine std::exception* zu fangen. Aber niemand erwartet einen Zeiger auf eine Ausnahme. Selbst wenn Sie das dokumentieren, was Ihre Funktion tut, und die Leute die Dokumentation lesen, können sie trotzdem vergessen und versuchen, stattdessen einen Verweis auf ein std::exception Objekt zu erfassen.

+25

Das Werfen von 'new std :: exception' ist nur dann korrekt, wenn die Aufrufstelle erwartet, dass ein Zeiger abgefangen wird und erwartet wird, die Verwaltung der Zuweisungsausnahme zu übernehmen UND es wird niemals Fälle geben, in denen Ihre Funktion aufgerufen wird durch etwas, das nicht explizit den richtigen Zeiger abfängt ('catch (...)' oder gar keine Behandlung), sonst kommt es zu einem Objektleck. Kurz gesagt, dies kann angenähert werden als "nie". –

+0

Es ist merkwürdig, wie diese Antwort akzeptiert wurde, wenn es wirklich @ CharlesBaileys * Kommentar * ist, dass die richtige Antwort ist. –

+0

@John: Das kam mir auch in den Sinn. Aber ich denke, der One-Two-Punch hat eine gute Wirkung, wenn ich die trockene Zusammenfassung gebe und Charles amüsant auf die verschiedenen Wege ausdehnt, auf denen die Menschen vergessen, damit umzugehen. Zu schade, dass Sie nicht den Ruf von neu gewählten Kommentaren bekommen. – Hurkyl

7

Die C++ FAQ hat eine schöne Diskussion über diese.

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

Im Grunde „, es sei denn ein guter Grund, es nicht fangen durch Bezugnahme auf, Vermeiden von Wert zu kontrollieren, weil das eine Kopie erzeugt und die Kopie ein anderes Verhalten haben kann als das, was geworfen wurde. Nur unter ganz besonderen Umständen sollten Sie den Zeiger fangen. "

+2

Wie üblich ist die FAQ schlecht formuliert. Sie können nach Wert oder Referenz abfangen. Ein Zeiger ist zufällig ein Wert (den Sie nach Wert oder Verweis abfangen). Denken Sie daran, dass der Typ "A" vom Typ "A *" verschieden ist. Wenn ich also "A"() wende, kann ich nicht mit "catch (A * e)" fangen, da es sich um einen völlig anderen Typ handelt. –

+0

Diese Links sind jetzt unterbrochen. – spanndemic

+1

Ich reparierte die Links @spannendemic – user1202136

Verwandte Themen