Ich neige dazu, meinem C ++ - Code viele Zusicherungen hinzuzufügen, um das Debuggen zu vereinfachen, ohne die Leistung von Release-Builds zu beeinträchtigen. Jetzt assert
handelt es sich um ein reines C-Makro, das ohne Berücksichtigung von C ++ - Mechanismen entwickelt wurde.
C ++ definiert dagegen std::logic_error
, was in Fällen ausgelöst werden soll, in denen ein Fehler in der Programmlogik vorliegt (daher der Name). Das Auslösen einer Instanz ist möglicherweise die perfekte Alternative zu C ++ assert
.
Das Problem ist , dass assert
und abort
sowohl das Programm sofort beenden , ohne Destruktoren aufrufen, damit die Bereinigung übersprungen, während eine Ausnahme werfen manuell fügt unnötige Laufzeitkosten. Eine Möglichkeit, dies zu umgehen, besteht darin, ein eigenes Assertionsmakro zu erstellen SAFE_ASSERT
, das genau wie das C-Gegenstück funktioniert, jedoch bei einem Fehler eine Ausnahme auslöst.
Ich kann mir drei Meinungen zu diesem Problem vorstellen:
- Halten Sie sich an Cs Behauptung. Da das Programm sofort beendet wird, spielt es keine Rolle, ob Änderungen korrekt abgewickelt werden. Ebenso
#define
schlecht ist die Verwendung von s in C ++. - Wirf eine Ausnahme und fange sie in main () ab . Das Überspringen von Destruktoren durch Code in einem beliebigen Status des Programms ist eine schlechte Praxis und muss unter allen Umständen vermieden werden, ebenso wie Aufrufe zum Beenden (). Wenn Ausnahmen ausgelöst werden, müssen sie abgefangen werden.
- Wirf eine Ausnahme und lass sie das Programm beenden. Eine Ausnahme beim Beenden eines Programms ist in Ordnung, und aufgrund
NDEBUG
dessen wird dies in einem Release-Build niemals passieren. Das Abfangen ist nicht erforderlich und macht Implementierungsdetails des internen Codes verfügbarmain()
.
Gibt es eine endgültige Antwort auf dieses Problem? Irgendeine professionelle Referenz?
Bearbeitet: Das Überspringen von Destruktoren ist natürlich kein undefiniertes Verhalten.
quelle
logic_error
ist der logische Fehler. Ein Fehler in der Programmlogik wird als Fehler bezeichnet. Sie lösen Fehler nicht, indem Sie Ausnahmen auslösen.static_assert
wo es angebracht ist, wenn Sie dies zur Verfügung haben.std::bug
?std::abort()
. Es wird nur ein Signal ausgelöst, das den Prozess beendet.Antworten:
Behauptungen sind im C ++ - Code völlig angemessen. Ausnahmen und andere Fehlerbehandlungsmechanismen sind nicht wirklich für das Gleiche wie Behauptungen gedacht.
Die Fehlerbehandlung ist für den Fall vorgesehen, dass ein Fehler möglicherweise behoben oder dem Benutzer ordnungsgemäß gemeldet werden kann. Wenn beispielsweise beim Lesen einer Eingabedatei ein Fehler auftritt, möchten Sie möglicherweise etwas dagegen tun. Fehler können aus Fehlern resultieren, aber sie können auch einfach die geeignete Ausgabe für eine bestimmte Eingabe sein.
Behauptungen beziehen sich beispielsweise auf die Überprüfung, ob die Anforderungen einer API erfüllt sind, wenn die API normalerweise nicht überprüft wird, oder auf die Überprüfung von Dingen, von denen der Entwickler glaubt, dass sie durch die Konstruktion garantiert sind. Wenn ein Algorithmus beispielsweise sortierte Eingaben erfordert, würden Sie dies normalerweise nicht überprüfen, aber Sie haben möglicherweise die Zusicherung, dies zu überprüfen, damit Debug-Builds diese Art von Fehler kennzeichnen. Eine Zusicherung sollte immer auf ein falsch funktionierendes Programm hinweisen.
Wenn Sie ein Programm schreiben, bei dem ein unsauberes Herunterfahren ein Problem verursachen kann, sollten Sie Behauptungen vermeiden. Undefiniertes Verhalten, das ausschließlich der C ++ - Sprache entspricht, ist hier kein solches Problem, da das Treffen einer Behauptung wahrscheinlich bereits das Ergebnis eines undefinierten Verhaltens oder der Verletzung einer anderen Anforderung ist, die verhindern könnte, dass eine Bereinigung ordnungsgemäß funktioniert.
Auch wenn Sie Behauptungen in Bezug auf eine Ausnahme implementieren, kann diese möglicherweise abgefangen und "behandelt" werden, obwohl dies dem eigentlichen Zweck der Behauptung widerspricht.
quelle
3
anstelle1
Ihres Codes übergibt , sollte er im Allgemeinen keine Zusicherung auslösen. Behauptungen sind nur Programmiererfehler, keine Benutzer der Bibliothek oder Anwendungsfehler.Zusicherungen dienen zum Debuggen . Der Benutzer Ihres Versandcodes sollte diese niemals sehen. Wenn eine Zusicherung getroffen wird, muss Ihr Code korrigiert werden.
Ausnahmen gelten für außergewöhnliche Umstände . Wenn einer angetroffen wird, kann der Benutzer nicht tun, was er will, kann aber möglicherweise an einer anderen Stelle fortfahren.
Die Fehlerbehandlung gilt für den normalen Programmablauf. Wenn Sie beispielsweise den Benutzer zur Eingabe einer Nummer auffordern und etwas Unparsables erhalten, ist dies normal , da Benutzereingaben nicht unter Ihrer Kontrolle stehen und Sie selbstverständlich immer mit allen möglichen Situationen umgehen müssen. (ZB Schleife, bis Sie eine gültige Eingabe haben und dazwischen "Entschuldigung, versuchen Sie es erneut" sagen.)
quelle
Assertions können verwendet werden, um interne Implementierungsinvarianten zu überprüfen, z. B. den internen Status vor oder nach der Ausführung einer Methode usw. Wenn Assertion fehlschlägt, bedeutet dies, dass die Logik des Programms fehlerhaft ist und Sie sich nicht davon erholen können. In diesem Fall ist das Beste, was Sie tun können, so schnell wie möglich zu brechen, ohne eine Ausnahme an den Benutzer zu übergeben. Was an Assertions (zumindest unter Linux) wirklich gut ist, ist, dass der Core-Dump als Ergebnis der Prozessbeendigung generiert wird und Sie so den Stack-Trace und die Variablen leicht untersuchen können. Dies ist viel nützlicher, um den Logikfehler zu verstehen, als eine Ausnahmemeldung.
quelle
Das Ausführen von Destruktoren aufgrund von alling abort () ist kein undefiniertes Verhalten!
Wenn es so wäre, wäre es ein undefiniertes Verhalten, auch anzurufen
std::terminate()
, und was wäre der Sinn, es bereitzustellen?assert()
ist in C ++ genauso nützlich wie in C. Behauptungen dienen nicht der Fehlerbehandlung, sondern dem sofortigen Abbruch des Programms.quelle
abort()
ist für den sofortigen Abbruch des Programms. Sie haben Recht, dass Zusicherungen nicht für die Fehlerbehandlung bestimmt sind. Assert versucht jedoch, den Fehler durch Abbrechen zu behandeln. Sollten Sie nicht stattdessen eine Ausnahme auslösen und den Anrufer den Fehler behandeln lassen, wenn dies möglich ist? Schließlich kann der Anrufer besser feststellen, ob es sich aufgrund des Ausfalls einer Funktion nicht lohnt, etwas anderes zu tun. Vielleicht versucht der Anrufer, drei Dinge zu tun, die nichts miteinander zu tun haben, und könnte trotzdem die beiden anderen Jobs erledigen und diesen einfach verwerfen.assert
ist zum Aufrufen definiertabort
(wenn die Bedingung falsch ist). Was das Auslösen von Ausnahmen betrifft, nein, das ist nicht immer angemessen. Einige Dinge können vom Anrufer nicht erledigt werden. Der Aufrufer kann nicht feststellen, ob ein logischer Fehler in einer Bibliotheksfunktion eines Drittanbieters behoben werden kann oder ob beschädigte Daten behoben werden können.IMHO, Behauptungen sind für die Überprüfung von Bedingungen, die, wenn verletzt, alles andere Unsinn machen. Und deshalb können Sie sich nicht von ihnen erholen, oder besser gesagt, die Wiederherstellung ist irrelevant.
Ich würde sie in 2 Kategorien einteilen:
Dies sind beides triviale Beispiele, aber nicht zu weit von der Realität entfernt. Denken Sie beispielsweise an naive Algorithmen, die negative Indizes für die Verwendung mit Vektoren zurückgeben. Oder eingebettete Programme in benutzerdefinierte Hardware. Oder eher, weil es nicht passiert .
Und wenn es solche Entwicklungsfehler gibt, sollten Sie sich nicht sicher sein, ob ein Wiederherstellungs- oder Fehlerbehandlungsmechanismus implementiert ist. Gleiches gilt für Hardwarefehler.
quelle