Sollte ein C ++ - Programm alle Ausnahmen abfangen und verhindern, dass Ausnahmen hinter main () auftauchen?


29

Mir wurde einmal geraten, dass ein C ++ - Programm letztendlich alle Ausnahmen abfangen sollte. Die Begründung lautete zu der Zeit im Wesentlichen, dass Programme, die es erlauben, dass Ausnahmen außerhalb main()eines seltsamen Zombie-Zustands auftauchen . Dies wurde mir vor einigen Jahren mitgeteilt, und im Nachhinein glaube ich, dass das beobachtete Phänomen auf die langwierige Erzeugung außergewöhnlich großer Core-Dumps aus dem fraglichen Projekt zurückzuführen war.

Das wirkte damals bizarr, aber überzeugend. Es war völlig unsinnig, dass C ++ Programmierer "bestraft", weil sie nicht alle Ausnahmen abfingen, aber die Beweise vor mir schienen dies zu untermauern. Für das fragliche Projekt schienen Programme, die nicht abgefangene Ausnahmen ausgelöst hatten, in einen seltsamen Zombie-Zustand überzugehen - oder, wie ich jetzt vermute, ein Prozess inmitten eines unerwünschten Core-Dumps ist ungewöhnlich schwer zu stoppen.

(Für alle, die sich fragen, warum dies zu dieser Zeit nicht offensichtlicher war: Das Projekt erzeugte eine große Menge an Ausgaben in mehreren Dateien aus mehreren Prozessen, die jede Art von aborted (core dumped)Nachricht effektiv verdeckten , und in diesem speziellen Fall war die Post-Mortem-Untersuchung von Core-Dumps nicht möglich Es ist keine wichtige Debugging - Technik, daher wurde nicht viel über Core - Dumps nachgedacht. Probleme mit einem Programm hingen normalerweise nicht vom Zustand ab, der von einem langlebigen Programm im Laufe der Zeit aus vielen Ereignissen angesammelt wurde, sondern von den anfänglichen Eingaben in ein kurzlebiges Programm (< 1 Stunde), daher war es praktischer, ein Programm mit denselben Eingaben aus einem Debugbuild oder in einem Debugger erneut auszuführen, um weitere Informationen zu erhalten.)

Derzeit bin ich mir nicht sicher, ob es einen großen Vorteil oder Nachteil gibt, Ausnahmen nur zu fangen, um das Verlassen von Ausnahmen zu verhindern main().

Der kleine Vorteil, den ich mir vorstellen kann, main()wenn ich zulasse, dass Ausnahmen in die Vergangenheit sprudeln, ist, dass das Ergebnis std::exception::what()auf dem Terminal gedruckt wird (zumindest mit GCC-kompilierten Programmen unter Linux). Auf der anderen Seite ist dies trivial zu erreichen, indem stattdessen alle Ausnahmen abgefangen std::exceptionund das Ergebnis gedruckt werden. std::exception::what()Wenn es wünschenswert ist, eine Nachricht von einer Ausnahme zu drucken, die nicht von std::exceptiondieser Ausnahme stammt, muss sie vor dem Verlassen abgefangen main()werden, um gedruckt zu werden die Nachricht.

Der bescheidene Nachteil, den ich mir main()vorstellen kann, um Ausnahmen zuzulassen, ist, dass unerwünschte Kerndumps erzeugt werden können. Bei einem Prozess, der viel Arbeitsspeicher benötigt, kann dies ziemlich ärgerlich sein, und für die Steuerung des Core-Dump-Verhaltens in einem Programm sind betriebssystemspezifische Funktionsaufrufe erforderlich. Wenn andererseits ein Core-Dump und ein Exit gewünscht werden, kann dies stattdessen jederzeit durch Aufrufen erreicht werden, std::abort()und ein Exit ohne Core-Dump kann jederzeit durch Aufrufen erreicht werden std::exit().

Anekdotischerweise glaube ich nicht, dass ich jemals die Standardnachricht gesehen habe, die what(): ...von einem weit verbreiteten Programm nach einem Absturz gedruckt wurde.

Was sind, wenn überhaupt, die starken Argumente dafür oder dagegen, dass C ++ - Ausnahmen in die Vergangenheit sprudeln main()?

Bearbeiten: Auf dieser Website gibt es viele allgemeine Fragen zur Ausnahmebehandlung. Meine Frage bezieht sich speziell auf C ++ - Ausnahmen, die nicht behandelt werden können und es bis jetzt geschafft haben. main()Vielleicht kann eine Fehlermeldung gedruckt werden, aber es handelt sich um einen Fehler, der sofort angezeigt wird.




@gnat Meine Frage ist etwas genauer. Es geht um C ++ - Ausnahmen, die unter keinen Umständen in einer Programmiersprache behandelt werden können, anstatt mit Ausnahmen umzugehen.
Praxeolitic

Bei Multithread-Programmen können die Dinge komplexer oder exotischer sein (siehe Ausnahmen)
Basile Starynkevitch

1
Die Software-Jungs für die Ariane 5 wünschten sich sicherlich, sie hätten alle Ausnahmen beim ersten Start abgefangen ...
Eric Towers

Antworten:


25

Ein Problem beim Übergehenlassen von Ausnahmen nach main besteht darin, dass das Programm mit einem Aufruf endet, bei std::terminatedem standardmäßig aufgerufen wird std::abort. Es ist nur die Implementierung definiert, die ausgeführt wird, wenn der Stapel vor dem Aufruf gelöscht wird, terminatesodass Ihr Programm beendet werden kann, ohne einen einzelnen Destruktor aufzurufen! Wenn Sie eine Ressource haben, die wirklich durch einen Destruktor-Aufruf wiederhergestellt werden musste, sind Sie in der Klemme ...


12
Wenn Sie eine Ressource haben, die wirklich durch einen Destruktoraufruf wiederhergestellt werden musste, befinden Sie sich in einer schwierigen Situation ... => Angesichts dessen, dass C ++ (leider) ziemlich absturzanfällig ist, und ohne zu erwähnen, dass das Betriebssystem möglicherweise entscheidet, das Programm zu beenden Ihr Programm muss jederzeit (z. B. OOM-Killer unter Unix) auf die Unterstützung von Abstürzen zugeschnitten sein.
Matthieu M.

@MatthieuM. Wollen Sie damit sagen, dass Destruktoren kein guter Ort für die Bereinigung von Ressourcen sind, die auch während eines Absturzes auftreten sollten?
Praxeolitic

6
@Praxeolitic: Ich sage, dass nicht alle Abstürze den Stack abwickeln, zum Beispiel std::abortnicht und somit fehlgeschlagene Behauptungen auch nicht. Es ist auch erwähnenswert, dass auf modernen Betriebssystemen das Betriebssystem selbst viele Ressourcen (Speicher, Dateihandles, ...) bereinigt, die an die Prozess-ID gebunden sind. Schließlich habe ich auch auf "Defense in Depth" hingewiesen: Es ist unzuverlässig zu erwarten, dass alle anderen Prozesse fehlerfrei sind und immer die Ressourcen freigeben, die sie erworben haben (Sitzungen, ordentliches Ende des Schreibens von Dateien, ...), für die Sie planen müssen it ...
Matthieu M.

3
@Praxeolitic Nein, Ihre Software darf bei einem Stromausfall keine Daten beschädigen . Und wenn Sie das schaffen, können Sie es auch schaffen, Daten nach einer unbehandelten Ausnahme nicht zu beschädigen.
user253751

1
@Praxeolitic: Eigentlich existiert dies. Sie möchten die WM_POWERBROADCASTNachricht abhören . Dies funktioniert nur, wenn Ihr Computer batteriebetrieben ist (wenn Sie einen Laptop oder eine USV verwenden).
Brian

28

Der Hauptgrund dafür, dass Ausnahmen nicht ignoriert werden, mainbesteht darin, dass Sie sonst die Kontrolle darüber verlieren, wie das Problem Ihren Benutzern gemeldet wird.

Für ein Programm, das nicht für längere Zeit verwendet oder weit verbreitet werden soll, kann es akzeptabel sein, dass unerwartete Fehler gemeldet werden, wie auch immer das Betriebssystem dies beschließt (z. B. das Anzeigen eines Dialogfelds mit In-Your-Face-Fehlern unter Windows ).

Bei Programmen, die Sie verkaufen oder die von einer Organisation, die einen guten Ruf hat, der Öffentlichkeit zur Verfügung gestellt werden, ist es im Allgemeinen besser, auf eine nette Art und Weise zu melden, dass Sie auf ein unerwartetes Problem gestoßen sind, und zu versuchen, so viel wie möglich davon zu sparen Benutzerdaten wie möglich. Wenn Sie nicht einen halben Arbeitstag Ihres Benutzers verlieren und nicht unerwartet abstürzen, sondern nur halbwegs ordnungsgemäß herunterfahren, ist dies in der Regel viel besser für Ihren Unternehmensruf als die Alternative.


Sind Sie sicher, dass das Standardverhalten einer C ++ - Ausnahmebedingung main() viel mit dem Betriebssystem zu tun hat? Das Betriebssystem weiß möglicherweise nichts von C ++. Meine Vermutung war, dass es durch den Code bestimmt wird, den der Compiler irgendwo in das Programm einfügt.
Praxeolitic

5
@Praxeolitic: Es ist mehr so, dass das Betriebssystem Konventionen festlegt und der Compiler Code generiert, um diese Konventionen zu erfüllen, wenn ein Programm unerwartet beendet wird. Das Fazit ist, dass ein unerwarteter Programmabbruch nach Möglichkeit vermieden werden sollte.
Bart van Ingen Schenau

Sie verlieren nur im Rahmen von std C ++ die Kontrolle. Eine implementierungsspezifische Möglichkeit zur Behandlung solcher "Abstürze" sollte verfügbar sein. C ++ läuft normalerweise nicht im luftleeren Raum.
Martin Ba

Dieser In-Your-Face-Fehlerdialog kann in der Registrierung für das, was es wert ist, ein- und ausgeschaltet werden. Dazu benötigen Sie jedoch die Kontrolle über die Registrierung. Die meisten Programme werden nicht im Kiosk-Modus ausgeführt (nachdem sie übernommen wurden) das Betriebssystem).
PerryC

11

TL; DR : Was steht in der Spezifikation?


Ein technischer Umweg ...

Wenn eine Ausnahme ausgelöst wird und kein Handler dafür bereit ist:

  • Es ist implementierungsdefiniert, ob der Stack abgewickelt wird oder nicht
  • std::terminate wird aufgerufen, was standardmäßig abbricht
  • Abhängig von Ihrer Umgebungseinstellung hinterlässt der Abbruch möglicherweise einen Absturzbericht

Letzteres kann bei sehr seltenen Fehlern hilfreich sein (da die Reproduktion zeitaufwändig ist).


Ob alle Ausnahmen erfasst werden sollen oder nicht, ist letztendlich eine Frage der Spezifikation:

  • Ist festgelegt, wie Benutzerfehler gemeldet werden sollen? (Ungültiger Wert, ...)
  • Ist festgelegt, wie Funktionsfehler gemeldet werden sollen? (fehlendes Zielverzeichnis, ...)
  • Ist festgelegt, wie technische Fehler zu melden sind? (Behauptung geht auf, ...)

Für jedes Produktionsprogramm sollte dies angegeben werden, und Sie sollten die Spezifikation befolgen (und möglicherweise argumentieren, dass es geändert werden soll).

Für schnell zusammengewürfelte Programme, die nur von Fachleuten (Ihnen, Ihren Teamkollegen) verwendet werden dürfen, ist beides in Ordnung. Ich empfehle, es abstürzen zu lassen und die Umgebung so einzurichten, dass ein Bericht erstellt wird, oder nicht, je nach Ihren Anforderungen.


1
Dies. Die entscheidenden Faktoren wt. Sind grundsätzlich außerhalb des Geltungsbereichs von C ++.
Martin Ba

4

Eine Ausnahme, die Sie abfangen, gibt Ihnen die Möglichkeit, eine nette Fehlermeldung auszudrucken oder sogar zu versuchen, den Fehler zu beheben (möglicherweise indem Sie die Anwendung einfach neu starten).

In C ++ enthält eine Ausnahme jedoch keine Informationen zum Programmstatus, als sie ausgelöst wurde. Wenn Sie es abfangen, werden alle derartigen Zustände vergessen, wohingegen, wenn Sie das Programm abstürzen lassen, der Zustand normalerweise noch vorhanden ist und aus dem Programm-Dump gelesen werden kann, was das Debuggen erleichtert.

Es ist also ein Kompromiss.


4

In dem Moment, in dem Sie wissen, dass Sie den Vorgang abbrechen müssen, rufen Sie std::terminatebereits an, um weiteren Schaden einzudämmen.

Wenn Sie wissen, dass Sie sich sicher entspannen können, tun Sie dies stattdessen. Denken Sie daran, dass das Abwickeln des Stapels nicht garantiert ist, wenn eine Ausnahme niemals abgefangen wird.

Wenn Sie den Fehler sicherer melden / protokollieren können, als dies das System alleine tun wird, fahren Sie fort.
Aber seien Sie wirklich sicher, dass Sie die Sache dabei nicht versehentlich verschlimmern.

Es ist oft zu spät, um Daten zu speichern, wenn Sie einen nicht behebbaren Fehler feststellen. Dies hängt jedoch vom jeweiligen Fehler ab.
Wie auch immer, wenn Ihr Programm für eine schnelle Wiederherstellung geschrieben wurde, ist es möglicherweise die beste Möglichkeit, es zu beenden, auch wenn es nur ein normales Herunterfahren ist.


1

Die meiste Zeit ist es gut, anmutig zusammenzustoßen - aber es gibt Kompromisse. Manchmal ist es gut, wenn man abstürzt. Ich sollte erwähnen, dass ich hauptsächlich über das Debuggen im großen Stil nachdenke. Für ein einfaches Programm - obwohl dies immer noch nützlich sein mag - ist es bei weitem nicht so hilfreich wie für ein sehr komplexes Programm (oder die Interaktion mehrerer komplexer Programme).

Sie möchten nicht in der Öffentlichkeit abstürzen (obwohl dies wirklich unvermeidlich ist - Programme stürzen ab, und ein wirklich mathematisch überprüfbares, nicht abstürzendes Programm ist nicht das, worüber wir hier sprechen). Denken Sie an Bill Gates BSODing mitten in einer Demo - schlecht, oder? Trotzdem können wir versuchen herauszufinden, warum wir abgestürzt sind und nicht noch einmal auf die gleiche Weise.

Ich habe eine Funktion der Windows-Fehlerberichterstattung aktiviert, mit der bei nicht behandelten Ausnahmen lokale Absturzabbilder erstellt werden. Es wirkt Wunder, wenn Sie die Symboldateien haben, die mit Ihrem (abstürzenden) Build verbunden sind. Interessanterweise möchte ich, weil ich dieses Tool eingerichtet habe, mehr abstürzen - weil ich aus jedem Absturz lerne. Dann kann ich die Fehler beheben und weniger abstürzen.

Deshalb möchte ich, dass meine Programme ganz nach oben springen und abstürzen. In Zukunft möchte ich vielleicht alle meine Ausnahmen mit Würde essen - aber nicht, wenn ich sie für mich arbeiten lassen kann.

Weitere Informationen zu lokalen Absturzabbildern finden Sie hier: Sammeln von Abbildern im Benutzermodus


-6

Wenn eine Ausnahme nach main () auftritt, stürzt Ihre Anwendung ab, und meiner Meinung nach sollte eine App niemals abstürzen. Wenn es in Ordnung ist, eine App an einem Ort zum Absturz zu bringen, warum dann nicht überall? Warum sich überhaupt mit der Ausnahmebehandlung beschäftigen? (Sarkasmus, was nicht wirklich darauf hindeutet ...)

Möglicherweise haben Sie eine globale Try / Catch-Funktion, die eine elegante Meldung ausgibt, die dem Benutzer mitteilt, dass ein nicht behebbarer Fehler aufgetreten ist und er eine E-Mail an Bob in der IT senden muss. Es ist trivial zu verhindern, und Sie können einen Benutzer darüber informieren, was gerade passiert ist und wie man Dinge behebt, damit es nicht wieder passiert.

Crashing ist eine reine Amateurstunde.


8
Also, besser so tun, als ob alles richtig funktioniert und stattdessen den gesamten permanenten Speicher mit Kauderwelsch überschreiben?
Deduplikator

1
@ Deduplicator: Nein: Sie können die Ausnahme immer abfangen und damit umgehen.
Giorgio

5
Wenn Sie wissen, wie man damit umgeht, werden Sie wahrscheinlich lange vor dem Aufstehen damit umgehen main. Wenn Sie nicht wissen, wie Sie damit umgehen sollen, ist es offensichtlich ein wirklich schrecklicher Fehler, so zu tun, als ob Sie es tun.
Deduplikator

8
Abstürze sind jedoch völlig unprofessionell und inakzeptabel. => Zeit mit nutzlosen Funktionen zu verbringen, ist unprofessionell: Abstürze sind nur von Bedeutung, wenn sie für den Endbenutzer von Bedeutung sind, wenn nicht, dann Abstürze zulassen (und einen Absturzbericht mit einem Speicherauszug abrufen). ist wirtschaftlicher.
Matthieu M.

Matthieu M. Wenn es wirklich so viel Mühe kostet, einen Fehler zu protokollieren, offene Dateien zu speichern und eine freundliche Nachricht auf den Bildschirm zu malen, weiß ich nicht, was ich sagen soll. Wenn Sie der Meinung sind, dass es sinnlos ist, ordnungsgemäß zu scheitern, sollten Sie sich wahrscheinlich an jemanden wenden, der technischen Support für Ihren Code leistet.
Roger Hill
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.