abbrechen, beenden oder beenden?

112

Was ist der Unterschied zwischen diesen drei und wie soll ich das Programm im Falle einer Ausnahme beenden, die ich nicht richtig behandeln kann?

Wir können nichts tun
quelle
3
Dies ist kein Duplikat, sondern eine Teilmenge mit einigen guten Antworten stackoverflow.com/questions/397075/… und es wurde auch mit C ++ markiert!
Ellie Kesselman
std::abortist sinnvoll, wenn eine Ausnahme in einem Destruktor nicht behoben werden kann.
Daniel
1
Weitere Informationen finden std::terminateSie in Andrzejs ausgezeichnetem C ++ - Blog: akrzemi1.wordpress.com/2011/09/28/who-calls-stdterminate , akrzemi1.wordpress.com/2011/10/05/using-stdterminate
Ohad Schneider

Antworten:

2

Mein Rat wäre, keinen von ihnen zu verwenden. Stattdessen catchdie Ausnahmen , die Sie nicht bearbeiten können main()und einfach returnvon dort. Dies bedeutet, dass Sie garantiert sind, dass das Abwickeln des Stapels korrekt erfolgt und alle Destruktoren aufgerufen werden. Mit anderen Worten:

int main() {
    try {
       // your stuff
    }
    catch( ... ) {
       return 1;    // or whatever
    }
}
SS Anne
quelle
8
@Neil: Grundsätzlich einverstanden, aber Ausnahmen, die das Programm nicht verarbeiten kann, sollten gemeldet und erneut ausgelöst werden. Lass die App abstürzen.
John Dibling
13
Um sicherzustellen, dass sich der Stapel abwickelt, sollten Sie immer in main fangen. Aber ich würde wieder vom Haken werfen. Da einige Betriebssysteme die Möglichkeit haben, die Debugging-Infrastruktur automatisch aufzurufen, wenn Sie in Debug kompiliert haben.
Martin York
5
Ausnahmen, die selbst von einem Top-Level-Handler nicht abgefangen werden, können eine Systemberichterstellungsfunktion aufrufen, die den Prozess speichert und den Ausnahmebericht zur Aufmerksamkeit der Entwickler hochlädt, z. B. Windows-Fehlerberichte, Mac OS X-Fehlerberichte und Fehlerprotokolle für iPhone-Anwendungen.
JBRWilkinson
6
@John Der Grund, warum es für mich überraschend ist, ist, dass es, obwohl es angesichts der tatsächlichen Implementierung von Ausnahmen durchaus sinnvoll ist, die Abstraktion bricht, dass eine Ausnahme "den Stapel weitergibt", bis ein geeigneter Handler gefunden wird (oder beendet wird) namens). Undichte Abstraktionen sind zwar oft unvermeidlich, aber bei ihrer Begegnung notwendigerweise überraschend.
Tyler McHenry
11
-1, weil dies nicht die halbe Frage beantwortet. "Was ist der Unterschied zwischen [Abbrechen, Beenden oder Beenden?]" Dies ist eine bessere Antwort: stackoverflow.com/a/397081/353094 Auch stackoverflow.com/a/2820407/353094 ist eine gute Antwort.
leetNightshade
149
  • Abbruch zeigt ein "abnormales" Programmende an und löst das POSIX-Signal SIGABRT aus. Dies bedeutet, dass jeder Handler, den Sie für dieses Signal registriert haben, aufgerufen wird, obwohl das Programm in beiden Fällen immer noch Nachwörter beendet. Normalerweise würden Sie abortin einem C-Programm einen unerwarteten Fehlerfall beenden, bei dem der Fehler wahrscheinlich ein Fehler im Programm ist und nicht eine schlechte Eingabe oder ein Netzwerkfehler. Zum Beispiel könnten Sie, abortwenn eine Datenstruktur einen NULL-Zeiger enthält, wenn dies logischerweise niemals passieren sollte.

  • exit zeigt ein "normales" Ende des Programms an, obwohl dies möglicherweise immer noch auf einen Fehler hinweist (aber nicht auf einen Fehler). Mit anderen Worten, Sie könnten exiteinen Fehlercode erhalten, wenn der Benutzer Eingaben gemacht hat, die nicht analysiert werden konnten, oder wenn eine Datei nicht gelesen werden konnte. Ein Exit-Code von 0 zeigt Erfolg an. exitRuft optional auch Handler auf, bevor das Programm beendet wird. Diese sind bei den atexitund on_exitFunktionen registriert .

  • std :: terminate wird in einem C ++ - Programm automatisch aufgerufen, wenn eine nicht behandelte Ausnahme vorliegt. Dies ist im Wesentlichen das C ++ - Äquivalent zu abort, vorausgesetzt, Sie melden alle Ihre außergewöhnlichen Fehler, indem Sie Ausnahmen auslösen. Dies ruft einen Handler auf, der von der std::set_terminateFunktion festgelegt wird, die standardmäßig einfach aufruft abort.

In C ++ möchten Sie normalerweise einen Aufruf abortoder einen exitFehler vermeiden , da Sie besser eine Ausnahme auslösen und den Code weiter oben im Aufrufstapel entscheiden lassen, ob das Beenden des Programms angemessen ist oder nicht. Ob Sie exitfür den Erfolg verwenden oder nicht, ist eine Frage der Umstände - ob es sinnvoll ist, das Programm an einem anderen Ort als der return-Anweisung in zu beenden main.

std::terminatesollte auch in C ++ als letztes Tool zur Fehlerberichterstattung betrachtet werden. Das Problem dabei std::terminateist, dass der Terminate-Handler keinen Zugriff auf die Ausnahme hat, die nicht behandelt wurde. Daher kann nicht festgestellt werden, um was es sich handelt. Normalerweise ist es viel besser, wenn Sie die gesamte Hauptleitung in einen try { } catch (std::exception& ex) { }Block einwickeln . Zumindest können Sie dann weitere Informationen zu Ausnahmen melden, die von abgeleitet wurden std::exception(obwohl Ausnahmen, von denen nicht abgeleitet wird, std::exceptionnatürlich immer noch unbehandelt bleiben).

Das Umschließen des Body von mainIn try { } catch(...) { }ist nicht viel besser als das Festlegen eines Terminate-Handlers, da Sie wiederum keinen Zugriff auf die betreffende Ausnahme haben. Bearbeiten: Laut Neil Butterworths Antwort hat der Vorteil, dass der Stapel in diesem Fall abgewickelt wird, was (etwas überraschend) für eine nicht behandelte Ausnahme nicht gilt.

Tyler McHenry
quelle
10
Können Sie diese Antwort mit C ++ 11-Informationen aktualisieren? Es scheint, dass es jetzt Möglichkeiten gibt, die Ausnahme im catch (...) und im terminate-Handler zu erhalten.
Klaim
1
In C ++ die Prozedur beendet hat Zugriff auf die Ausnahme haben durch std::current_exception(). Siehe Beispiel hier: akrzemi1.wordpress.com/2011/10/05/using-stdterminate
anorm
Es spielt keine Rolle, dass Sie die aktuelle Ausnahme erhalten können, da Sie sie nicht überprüfen können. Alles was Sie tun können, ist es erneut zu werfen.
SeattleCpp
2
@seattlecpp Sie können es erneut werfen und einen Verweis darauf fangen, den Sie dann überprüfen können
gpeche
16

std :: abort und std :: exit (und mehr: std :: _ Exit, std :: quick_exit) sind nur Funktionen auf niedrigerer Ebene. Sie verwenden sie, um dem Programm mitzuteilen, was genau es tun soll: welche Destruktoren (und ob) aufgerufen werden sollen, welche anderen Bereinigungsfunktionen aufgerufen werden sollen, welcher Wert zurückgegeben werden soll usw.

std :: terminate ist eine Abstraktion auf höherer Ebene: Sie wird (entweder zur Laufzeit oder von Ihnen) aufgerufen, um anzuzeigen, dass ein Fehler im Programm aufgetreten ist und dass es aus irgendeinem Grund nicht möglich ist, eine Ausnahme auszulösen. Die Notwendigkeit hierfür tritt normalerweise auf, wenn ein Fehler im Ausnahmemechanismus selbst auftritt. Sie können ihn jedoch jederzeit verwenden, wenn Sie nicht möchten, dass Ihr Programm über den angegebenen Fehler hinaus fortgesetzt wird. Ich habe die vollständige Liste der Situationen zusammengestellt, in denen std :: terminate in meinem Beitrag aufgerufen wird. Es ist nicht angegeben, was std :: terminate tut, da Sie die Kontrolle darüber haben. Sie können das Verhalten konfigurieren, indem Sie beliebige Funktionen registrieren. Die Einschränkungen, die Sie haben, bestehen darin, dass die Funktion nicht zur Fehlerstelle zurückkehren und nicht über eine Ausnahme beendet werden kann. Technisch gesehen können Sie jedoch sogar Ihre Nachrichtenpumpe im Inneren starten. Eine Liste nützlicher Dinge, die Sie im Inneren tun können, finden Sie in meinem anderen Beitrag .

Beachten Sie insbesondere, dass std :: terminate in Kontexten, in denen std :: terminate aufgrund einer ausgelösten Ausnahme aufgerufen wird, die nicht behandelt werden konnte, als Ausnahmebehandlungsroutine betrachtet wird. Sie können die Ausnahme überprüfen und mithilfe von C ++ überprüfen 11 Verwenden von std :: rethrow_exception und std :: current_exception. Es ist alles in meinem Beitrag .

Andrzej
quelle
Ist es ratsam, einen Bereinigungshandler zu haben, falls das Programm aufgrund von Systemsignalen beendet wird? Beispielsweise führt ein ungültiger Speicherzugriff zur Erzeugung eines SIGSEGV-Signals. Ist es in diesem Fall gut, das Programm einfach beenden zu lassen und die Kerndatei zu haben oder einen Signalhandler zu registrieren, um die Bereinigung durchzuführen? Gibt es Bedenken hinsichtlich einer Bereinigung während der Verarbeitung von Systemsignalen im Vergleich zu einer Bereinigung während der Verarbeitung von std :: terminate?
Kartik Trivikram
12

quick_exit () !

Wenn Ihr Programm über mehrere Threads verfügt, führt der Aufruf exit()höchstwahrscheinlich zu einem Absturz, da std::threadversucht wird, globale / statische Objekte zu zerstören, ohne ihre Threads zu verlassen.

Wenn Sie einen Fehlercode zurückgeben und das Programm (mehr oder weniger) normal beenden möchten, rufen Sie quick_exit()Multithread-Programme auf. Bei abnormaler Beendigung (ohne dass Sie den Fehlercode angeben können) abort()oder std::terminate()kann aufgerufen werden.

Hinweis: quick_exit () wurde von MSVC ++ bis Version 2015 nicht unterstützt .

Serge Rogatch
quelle
4
  • Mit terminate haben Sie die Möglichkeit zu registrieren, was passieren wird, wenn es aufgerufen wird. Sollte einer der beiden anderen sein.
  • exit ist ein normaler Exit, mit dem ein Exit-Status angegeben werden kann. Von at_exit () registrierte Handler werden ausgeführt
  • Abbruch ist ein abnormaler Ausgang. Das einzige, was ausgeführt wird, ist der Signalhandler für SIGABRT.
Ein Programmierer
quelle
4
  • terminate () wird automatisch aufgerufen, wenn eine Ausnahme auftritt, die nicht behandelt werden kann. Standardmäßig ruft terminate () abort () auf. Sie können ein benutzerdefiniertes Handle mit der Funktion set_terminate () festlegen.

    abort () sendet das SIGABRT-Signal.

    exit () ist nicht unbedingt eine schlechte Sache. Es beendet die Anwendung erfolgreich und ruft atexit () -Funktionen in LIFO-Reihenfolge auf. Normalerweise sehe ich dies nicht in C ++ - Anwendungen, aber ich sehe es in vielen Unix-basierten Anwendungen, in denen am Ende ein Exit-Code gesendet wird. Normalerweise zeigt ein Exit (0) einen erfolgreichen Lauf der Anwendung an.

Daniel
quelle
8
Erfolglos! Sowohl unter Unix als auch unter DOS zeigt exit (0) den Erfolg an und jeder andere an exit () übergebene Wert zeigt einen Fehler an, nicht umgekehrt!
Richard Barrell