Das Ausnahmesystem in C++ basiert auf dem Mechanismus try-catch und dem Schlüsselwort throw. Bei einer Ausnahmebedingung (z. B. Ressourcenfehler, Verletzung eines Invarianten) wird innerhalb des try-Blocks eine Ausnahme durch den Operator throw ausgelöst. Die Suche nach dem passenden Handler (catch) erfolgt durch das Durchlaufen des Stacks bis zum ersten übereinstimmenden Typ.
Wichtige Details:
std::exception abgeleitet ist).noexcept eingeführt.Entwurf:
catch(const std::exception& e).Beispielcode:
#include <iostream> #include <stdexcept> void mayFail(bool fail) { if (fail) throw std::runtime_error("Prozessfehler"); } int main() { try { mayFail(true); } catch (const std::exception& ex) { std::cout << "Ausnahme erfasst: " << ex.what() << std::endl; } }
Was passiert, wenn eine Ausnahme aus einem Destruktor während der Übertragung einer anderen Ausnahme nach oben im Stack geworfen wird?
Antwort: Es erfolgt ein unerwartetes Programmende durch den Aufruf von std::terminate(), da während der Verarbeitung einer Ausnahme das Generieren einer neuen Ausnahme (double exception) nicht zulässig ist, da dies das Stack Unwinding stört.
Beispiel:
struct CrashOnDestruct { ~CrashOnDestruct() noexcept(false) { throw std::runtime_error("Fehler im Destruktor!"); } }; void func() { CrashOnDestruct obj; throw std::logic_error("Fehlerverarbeitung"); } int main() { try { func(); } catch (...) { } } // Beendet sich durch std::terminate()
Geschichte
In einer großen Finanzanwendung wurden Ausnahmen aus dem Destruktor einer Wrapper-Klasse für die Datenbank geworfen. Eine der zentralen Transaktionen löste bei einem Fehler eine Ausnahme aus, der Destruktor wurde beim Unwind-Aufruf aufgerufen, der ebenfalls einen Fehler warf. Die gesamte Anwendung wurde unerwartet beendet, und die Arbeit der Benutzer ging verloren. Die Entwickler mussten dringend die throw-Anweisungen in den Destruktoren durch Protokollierung und korrekte Behandlung ersetzen.
Geschichte
Ein Mikrodienst bearbeitete Ausnahmen des Typs: catch(std::exception). Einer der Threads warf Ausnahmen benutzerdefinierter Typen (die nicht von std::exception abgeleitet waren). Diese Fehler wurden nicht gefangen, was zu einem unerwarteten Verbindungsabbruch und Speicherlecks führte.
Geschichte
Das Fehlen des Spezifizierers noexcept in den Methoden zum Bewegen von Containern führte dazu, dass die Standardbibliothek eine langsame Kopie anstelle einer schnellen Move-Operation verwendete; ein erheblicher Leistungsabfall wurde erst bei Lasttests festgestellt.