Ausnahmen
Die Ausnahmebehandlung bietet eine Möglichkeit, die Kontrolle und Informationen von einem Punkt in der Ausführung eines Programms zu einem Handler zu übertragen, der mit einem zuvor durchlaufenen Punkt der Ausführung assoziiert ist (mit anderen Worten, die Ausnahmebehandlung überträgt die Kontrolle den Aufrufstapel hinauf).
Die Auswertung eines throw Ausdrucks wirft eine Ausnahme. Ausnahmen können auch in anderen Kontexten geworfen werden.
Damit eine Ausnahme abgefangen werden kann, muss der throw Ausdruck innerhalb eines try Blocks stehen und der try Block muss einen Handler enthalten, der mit dem Typ des Ausnahmeobjekts übereinstimmt.
Bei der Deklaration einer Funktion können die folgenden Spezifikationen angegeben werden, um die Typen der Ausnahmen zu beschränken, die eine Funktion werfen darf.
| (bis C++17) |
| (seit C++11) |
Fehler, die während der Ausnahmebehandlung auftreten, werden von std::terminate und std::unexpected(bis C++17) behandelt.
Inhalt |
[bearbeiten] Verwendung
Während der throw Ausdruck verwendet werden kann, um die Kontrolle aus beliebigen Gründen zu einem beliebigen Codeblock auf dem Ausführungsstapel zu übertragen (ähnlich wie bei std::longjmp), ist seine beabsichtigte Verwendung die Fehlerbehandlung.
[bearbeiten] Fehlerbehandlung
Das Werfen einer Ausnahme wird verwendet, um Fehler aus Funktionen zu signalisieren, wobei "Fehler" typischerweise auf nur die folgenden beschränkt sind[1][2][3]
- Fehler bei der Einhaltung der Nachbedingungen, wie z. B. das Nicht-Erzeugen eines gültigen Rückgabewertobjekts.
- Fehler bei der Einhaltung der Vorbedingungen einer anderen Funktion, die aufgerufen werden muss.
- (für nicht-private Member-Funktionen) Fehler bei der (Wieder-)Herstellung einer Klasseninvarianten.
Insbesondere bedeutet dies, dass Fehler von Konstruktoren (siehe auch RAII) und den meisten Operatoren durch das Werfen von Ausnahmen gemeldet werden sollten.
Darüber hinaus verwenden sogenannte *Wide-Contract*-Funktionen Ausnahmen, um inakzeptable Eingaben anzuzeigen, z. B. hat std::basic_string::at keine Vorbedingungen, wirft aber eine Ausnahme, um einen ungültigen Index anzuzeigen.
[bearbeiten] Ausnahmensicherheit
Nachdem der Fehlerzustand von einer Funktion gemeldet wurde, können zusätzliche Garantien bezüglich des Zustands des Programms gegeben werden. Die folgenden vier Stufen der Ausnahmegarantie werden im Allgemeinen anerkannt[4][5][6], die echte Obermengen voneinander sind.
- Nothrow- (oder Nofail-) Ausnahmegarantie — die Funktion wirft niemals Ausnahmen. Nothrow (Fehler werden anderweitig gemeldet oder verborgen) wird von Destruktoren und anderen Funktionen erwartet, die während des Stapel-Unwinding aufgerufen werden können. Die Destruktoren sind standardmäßig
noexcept.(seit C++11) Nofail (die Funktion ist immer erfolgreich) wird von Swaps, Move-Konstruktoren und anderen Funktionen erwartet, die von denen verwendet werden, die eine starke Ausnahmegarantie bieten. - Starke Ausnahmegarantie — Wenn die Funktion eine Ausnahme wirft, wird der Zustand des Programms auf den Zustand vor dem Funktionsaufruf zurückgerollt (z. B. std::vector::push_back).
- Grundlegende Ausnahmegarantie — Wenn die Funktion eine Ausnahme wirft, befindet sich das Programm in einem gültigen Zustand. Keine Ressourcen werden geleakt, und alle Objektinvarianten sind intakt.
- Keine Ausnahmegarantie — Wenn die Funktion eine Ausnahme wirft, befindet sich das Programm möglicherweise nicht in einem gültigen Zustand: Ressourcenlecks, Speicherbeschädigung oder andere invarianzenzerstörende Fehler können aufgetreten sein.
Generische Komponenten können zusätzlich eine *Ausnahme-neutrale Garantie* bieten: Wenn eine Ausnahme von einem Template-Parameter geworfen wird (z. B. vom `Compare`-Funktionsobjekt von std::sort oder vom Konstruktor von `T` in std::make_shared), wird sie unverändert an den Aufrufer weitergegeben.
[bearbeiten] Ausnahmeobjekte
Während Objekte jedes vollständigen Typs und cv-Zeiger auf void als Ausnahmeobjekte geworfen werden können, werfen alle Standardbibliotheksfunktionen namenlose Objekte per Wert, und die Typen dieser Objekte leiten sich (direkt oder indirekt) von std::exception ab. Benutzerdefinierte Ausnahmen folgen normalerweise diesem Muster.[7][8][9]
Um unnötiges Kopieren des Ausnahmeobjekts und Objekt-Slicing zu vermeiden, ist die beste Vorgehensweise für Handler, per Referenz abzufangen.[10][11][12][13]
[bearbeiten] Anmerkungen
| Feature-Testmakro | Wert | Std | Feature |
|---|---|---|---|
__cpp_constexpr_exceptions |
202411L |
(C++26) | constexpr Ausnahmen |
[bearbeiten] Externe Links
|