ProgrammierungC++ Backend Entwickler

Wie funktioniert das Ausnahmesystem (exceptions) in C++ und wie unterscheidet es sich von anderen Sprachen? Wie sollte man Ausnahmen in industriellen Anwendungen richtig entwerfen?

Bestehen Sie Vorstellungsgespräche mit dem Hintsage-KI-Assistenten

Antwort

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:

  • Im Gegensatz zu Java oder C# können Ausnahmen in C++ jeden Typ haben (gewöhnlich eine Klasse, die von std::exception abgeleitet ist).
  • In C++ gibt es keine verpflichtende Ausnahmespezifikation (wie checked exceptions in Java), stattdessen wurde in C++11 und später der Spezifizierer noexcept eingeführt.
  • Bei einer Ausnahmeauslösung werden die Destruktoren für alle Objekte auf dem Stack zwischen dem Auswurfpunkt und dem Verarbeitungspunkt aufgerufen — es ist wichtig, RAII zu unterstützen.

Entwurf:

  • Werfen Sie nur Ausnahmen für "unerwartete" Fehler, verwenden Sie für andere Situationen Rückgabecode.
  • Erfassen Sie per konstantem Referenz — catch(const std::exception& e).
  • GNU empfiehlt, Objekte zu werfen, die kopiert werden, anstatt Zeiger (vermeiden Sie Speicher auf dem Heap).

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; } }

Fangfrage

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()

Beispiele für echte Fehler aufgrund mangelnden Wissens über die Feinheiten des Themas


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.