Exceptions
Die Ausnahmebehandlung bietet eine Möglichkeit, die Kontrolle und Informationen von einem Punkt in der Ausführung eines Programms an einen Handler zu übertragen, der mit einem zuvor von der Ausführung passierten Punkt verbunden ist (mit anderen Worten, die Ausnahmebehandlung überträgt die Kontrolle den Aufrufstapel hinauf).
Die Auswertung eines throw -Ausdrucks wirft eine Exception. Exceptions 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 zum Typ des Ausnahmeobjekts passt.
Bei der Deklaration einer Funktion können die folgenden Spezifikationen angegeben werden, um die Arten von Ausnahmen einzuschränken, die eine Funktion werfen darf:
| (bis C++17) |
| (seit C++11) |
Fehler, die während der Ausnahmebehandlung auftreten, werden durch std::terminate und std::unexpected (until C++17) behandelt.
Inhaltsverzeichnis |
Verwendung
Während throw -Ausdrücke verwendet werden können, um die Kontrolle an einen beliebigen Codeblock im Ausführungsstack zu übertragen, aus beliebigen Gründen (ähnlich wie std::longjmp ), ist ihre beabsichtigte Verwendung die Fehlerbehandlung.
Fehlerbehandlung
Das Werfen einer Exception wird verwendet, um Fehler aus Funktionen zu signalisieren, wobei "Fehler" typischerweise auf Folgendes beschränkt sind [1] [2] [3] :
- Verstöße gegen die Nachbedingungen, wie das Fehlschlagen bei der Erzeugung eines gültigen Rückgabewertobjekts.
- Verstöße gegen die Vorbedingungen einer anderen Funktion, die aufgerufen werden muss.
- (für nicht-private Memberfunktionen) Verstöße gegen die (Wieder-)Herstellung einer Klasseninvariante.
Insbesondere impliziert dies, dass Fehler von Konstruktoren (siehe auch RAII ) und der meisten Operatoren durch das Werfen von Exceptions gemeldet werden sollten.
Zusätzlich verwenden sogenannte wide contract Funktionen Ausnahmen, um unzulässige Eingaben anzuzeigen, zum Beispiel hat std::basic_string::at keine Vorbedingungen, wirft jedoch eine Ausnahme, um einen Index außerhalb des gültigen Bereichs anzuzeigen.
Ausnahmesicherheit
Nachdem eine Fehlerbedingung durch eine Funktion gemeldet wurde, können zusätzliche Garantien bezüglich des Programmzustands bereitgestellt werden. Die folgenden vier Stufen der Exception-Garantie werden allgemein anerkannt [4] [5] [6] , die strikte Obermengen voneinander sind:
-
Nothrow- (oder Nofail-) Ausnahmesicherheitsgarantie
— Die Funktion wirft niemals Ausnahmen. Nothrow (Fehler werden auf andere Weise gemeldet oder verborgen) wird von
Destruktoren
und anderen Funktionen erwartet, die während Stack-Unwinding aufgerufen werden können.
Standardmäßig sind
Destruktoren
noexcept. (seit C++11) Nofail (die Funktion ist immer erfolgreich) wird von Swaps, Move-Konstruktoren und anderen Funktionen erwartet, die von jenen verwendet werden, die die starke Ausnahmesicherheitsgarantie bieten. - Starke Ausnahmesicherheitsgarantie — Wenn die Funktion eine Ausnahme wirft, wird der Programmzustand auf den Zustand direkt vor dem Funktionsaufruf zurückgesetzt (zum Beispiel std::vector::push_back ).
- Grundlegende Ausnahmesicherheitsgarantie — Wenn die Funktion eine Ausnahme wirft, befindet sich das Programm in einem gültigen Zustand. Es treten keine Ressourcenlecks auf, und alle Objektinvarianten sind intakt.
- Keine Ausnahmesicherheitsgarantie — Wenn die Funktion eine Ausnahme wirft, befindet sich das Programm möglicherweise nicht in einem gültigen Zustand: Ressourcenlecks, Speicherbeschädigungen oder andere invariantenzerstörende Fehler können aufgetreten sein.
Generische Komponenten können zusätzlich eine
ausnahmeneutrale Garantie
bieten: Wenn eine Ausnahme von einem Template-Parameter ausgelöst wird (z.B. vom
Compare
-Funktionsobjekt in
std::sort
oder vom Konstruktor von
T
in
std::make_shared
), wird sie unverändert an den Aufrufer weitergegeben.
Exception-Objekte
Während Objekte jedes vollständigen Typs und cv-Zeiger auf void als Ausnahmeobjekte geworfen werden können, werfen alle Standardbibliotheksfunktionen unbenannte Objekte als Wert, und die Typen dieser Objekte sind (direkt oder indirekt) von std::exception abgeleitet. Benutzerdefinierte Ausnahmen folgen üblicherweise diesem Muster. [7] [8] [9]
Um unnötiges Kopieren des Ausnahmeobjekts und Object Slicing zu vermeiden, ist die beste Praxis für Handler, Ausnahmen per Referenz zu fangen. [10] [11] [12] [13]
Hinweise
| Feature-Test-Makro | Wert | Std | Feature |
|---|---|---|---|
__cpp_constexpr_exceptions
|
202411L
|
(C++26) | constexpr Exceptions |
Externe Links
|