Handling exceptions
Eine Exception kann von einem Handler behandelt werden.
Inhaltsverzeichnis |
Handler
catch
(
attr
(optional)
type-specifier-seq
declarator
)
compound-statement
|
(1) | ||||||||
catch
(
attr
(optional)
type-specifier-seq
abstract-declarator
(optional)
)
compound-statement
|
(2) | ||||||||
catch
(
...
)
compound-statement
|
(3) | ||||||||
| attr | - | (since C++11) beliebige Anzahl von attributes , gilt für den Parameter |
| type-specifier-seq | - | Teil einer formalen Parameterdeklaration, gleich wie in einer parameter list einer Funktion |
| declarator | - | Teil einer Parameterdeklaration, gleich wie in einer parameter list einer Funktion |
| abstract-declarator | - | Teil einer unbenannten Parameterdeklaration, gleich wie in einer parameter list einer Funktion |
| compound-statement | - | eine compound statement |
Die Parameterdeklaration in einem Handler beschreibt die Art(en) von Ausnahmen, die den Eintritt in diesen Handler verursachen können.
Wenn der Parameter als einer der folgenden Typen deklariert ist, ist das Programm fehlerhaft:
- ein incomplete type
- ein abstract class type
|
(seit C++11) |
- ein Zeiger auf einen unvollständigen Typ, außer (möglicherweise cv-qualifiziertem) void
- ein Lvalue-Referenz auf einen unvollständigen Typ
Wenn der Parameter als Typ "Array von
T
" oder Funktionstyp
T
deklariert ist, wird der Typ zu "Zeiger auf
T
" angepasst.
Ein Handler mit Parametertyp
T
kann als "ein Handler vom Typ
T
" abgekürzt werden.
Passende Ausnahmen
Jeder try -Block ist mit einer Anzahl von Handlern assoziiert, diese Handler bilden eine Handler-Sequenz. Wenn eine Exception aus einem try -Block geworfen wird, werden die Handler in der Sequenz in der Reihenfolge ihres Auftretens geprüft, um die Exception zu matchen.
Ein Handler passt auf ein
Exception-Objekt
vom Typ
E
, wenn eine der folgenden Bedingungen erfüllt ist:
-
Der Handler ist vom Typ "möglicherweise cv-qualifiziertes
T" oder "Lvalue-Referenz auf möglicherweise cv-qualifiziertesT", und eine der folgenden Bedingungen ist erfüllt:
-
-
EundTsind derselbe Typ (unter Vernachlässigung der Top-Level-cv-Qualifizierer). -
Tist eine eindeutige öffentliche Basisklasse vonE.
-
-
Der Handler ist vom Typ „möglicherweise cv-qualifiziert
T“ oder const T & , wobeiTein Zeiger- oder Zeiger-auf-Member-Typ ist und eine der folgenden Bedingungen erfüllt ist:
-
-
Eist ein Zeiger- oder Zeiger-auf-Mitglied-Typ, der durch mindestens eine der folgenden Konvertierungen inTumgewandelt werden kann:
-
- Eine Standardzeigerkonvertierung , die keine Konvertierungen zu Zeigern auf private, geschützte oder mehrdeutige Klassen beinhaltet.
-
| (seit C++17) |
-
-
- Eine Qualifikationskonvertierung .
-
|
(seit C++11) |
Der catch ( ... ) Handler fängt Ausnahmen jeglichen Typs ab. Falls vorhanden, kann er nur der letzte Handler in einer Handler-Sequenz sein. Dieser Handler kann verwendet werden, um sicherzustellen, dass keine unbehandelten Ausnahmen aus einer Funktion entweichen können, die eine nothrow exception guarantee bietet.
try { f(); } catch (const std::overflow_error& e) {} // dies wird ausgeführt, wenn f() std::overflow_error wirft (gleicher Typ-Regel) catch (const std::runtime_error& e) {} // dies wird ausgeführt, wenn f() std::underflow_error wirft (Basisklassen-Regel) catch (const std::exception& e) {} // dies wird ausgeführt, wenn f() std::logic_error wirft (Basisklassen-Regel) catch (...) {} // dies wird ausgeführt, wenn f() std::string oder int oder einen anderen nicht verwandten Typ wirft
Wenn keine Übereinstimmung unter den Handlern für einen try -Block gefunden wird, setzt sich die Suche nach einem passenden Handler in einem dynamisch umgebenden try -Block desselben Threads (since C++11) fort.
Wenn kein passender Handler gefunden wird, std::terminate wird aufgerufen; ob der Stack vor diesem Aufruf von std::terminate unwound wird, ist implementierungsdefiniert.
Behandlung von Ausnahmen
Wenn eine Ausnahme ausgelöst wird, wird die Kontrolle an den nächsten Handler mit passendem Typ übergeben; "nächster" bedeutet den Handler, für den der zusammengesetzte Anweisungsblock oder die Member-Initialisierungsliste (falls vorhanden) nach dem try -Schlüsselwort zuletzt vom Kontrollfluss betreten und noch nicht verlassen wurde.
Initialisierung des Handler-Parameters
Der in der Parameterliste (falls vorhanden) deklarierte Parameter vom Typ "möglicherweise cv-qualifiziertes
T
" oder "Lvalue-Referenz auf möglicherweise cv-qualifiziertes
T
" wird wie folgt vom
Exception-Objekt
vom Typ
E
initialisiert:
-
Wenn
Teine Basisklasse vonEist, wird der Parameter copy-initialized von einem Lvalue des TypsT, der das entsprechende Basisklassen-Subobjekt des Exception-Objekts bezeichnet. -
Andernfalls wird der Parameter von einem Lvalue des Typs
Ecopy-initialized, der das Exception-Objekt bezeichnet.
Die Lebensdauer des Parameters endet, wenn der Handler beendet wird, nach der Zerstörung aller Objekte mit automatischer Speicherdauer , die innerhalb des Handlers initialisiert wurden.
Wenn der Parameter als Objekt deklariert wird, wirken sich Änderungen an diesem Objekt nicht auf das Ausnahmeobjekt aus.
Wenn der Parameter als Referenz auf ein Objekt deklariert wird, sind alle Änderungen am referenzierten Objekt Änderungen am Ausnahmeobjekt und werden wirksam, falls dieses Objekt erneut ausgelöst wird.
Aktivierung des Handlers
Ein Handler wird als aktiv betrachtet, wenn die Initialisierung für den Parameter (falls vorhanden) des Handlers abgeschlossen ist.
Außerdem wird ein impliziter Handler als aktiv betrachtet, wenn std::terminate aufgrund eines Throw-Ausdrucks aufgerufen wird.
Ein Handler wird nicht mehr als aktiv betrachtet, wenn der Handler beendet wird.
Die Ausnahme mit dem zuletzt aktivierten Handler, der noch aktiv ist, wird als aktuell behandelte Ausnahme bezeichnet. Eine solche Ausnahme kann erneut ausgelöst werden .
Ablaufsteuerung
Die compound-statement eines Handlers ist eine control-flow-limited statement :
void f() { goto label; // Fehler try { goto label; // Fehler } catch (...) { goto label: // OK label: ; } }
Hinweise
Stack unwinding erfolgt während der Steuerung an einen Handler übergeben wird. Wenn ein Handler aktiv wird, ist das Stack Unwinding bereits abgeschlossen.
Die Ausnahme, die durch den throw -Ausdruck throw 0 ausgelöst wird, stimmt nicht mit einem Handler vom Typ Zeiger oder Zeiger-auf-Mitglied überein.
|
(seit C++11) |
Exception-Objekte können niemals Array- oder Funktionstypen haben, daher ist ein Handler für Referenzen auf Array- oder Funktionstyp niemals eine Übereinstimmung für ein Exception-Objekt.
Es ist möglich, Handler zu schreiben, die niemals ausgeführt werden können, zum Beispiel durch das Platzieren eines Handlers für eine finale abgeleitete Klasse nach einem Handler für eine entsprechende unzweideutige öffentliche Basisklasse:
try { f(); } catch (const std::exception& e) {} // wird ausgeführt, wenn f() std::runtime_error wirft catch (const std::runtime_error& e) {} // Dead Code!
Viele Implementierungen erweitern übermäßig die Auflösung von CWG issue 388 auf Handler von Referenzen auf Nicht-Konst-Zeigertypen:
int i; try { try { throw static_cast<float*>(nullptr); } catch (void*& pv) { pv = &i; throw; } } catch (const float* pf) { assert(pf == nullptr); // sollte erfolgreich sein, schlägt aber bei MSVC und Clang fehl }
Schlüsselwörter
Beispiel
Das folgende Beispiel demonstriert mehrere Anwendungsfälle der Handler:
#include <iostream> #include <vector> int main() { try { std::cout << "Throwing an integer exception...\n"; throw 42; } catch (int i) { std::cout << " the integer exception was caught, with value: " << i << '\n'; } try { std::cout << "Creating a vector of size 5... \n"; std::vector<int> v(5); std::cout << "Accessing the 11th element of the vector...\n"; std::cout << v.at(10); // vector::at() throws std::out_of_range } catch (const std::exception& e) // caught by reference to base { std::cout << " a standard exception was caught, with message: '" << e.what() << "'\n"; } }
Mögliche Ausgabe:
Throwing an integer exception... the integer exception was caught, with value: 42 Creating a vector of size 5... Accessing the 11th element of the vector... a standard exception was caught, with message: 'out_of_range'
Fehlerberichte
Die folgenden verhaltensändernden Fehlerberichte wurden rückwirkend auf zuvor veröffentlichte C++-Standards angewendet.
| DR | Angewendet auf | Verhalten wie veröffentlicht | Korrektes Verhalten |
|---|---|---|---|
| CWG 98 | C++98 | eine switch -Anweisung konnte die Steuerung in einen Handler übertragen | verboten |
| CWG 210 | C++98 | throw -Ausdrücke wurden mit den Handlern abgeglichen |
Ausnahmeobjekte werden
mit den Handlern abgeglichen |
| CWG 388 | C++98 |
eine Ausnahme vom Zeiger- oder Zeiger-auf-Member-Typ konnte
nicht durch eine const-Referenz auf einen anderen Typ abgeglichen werden |
abgleichbar gemacht
bei Konvertierbarkeit |
| CWG 1166 | C++98 |
das Verhalten war nicht spezifiziert, wenn ein Handler, dessen
Typ eine Referenz auf einen abstrakten Klassentyp ist, abgeglichen wird |
abstrakte Klassentypen sind
für Handler nicht erlaubt |
| CWG 1769 | C++98 |
wenn der Typ des Handlers eine Basis des Typs des
Ausnahmeobjekts ist, könnte ein konvertierender Konstruktor für die Initialisierung des Handler-Parameters verwendet werden |
der Parameter wird copy-initialized
vom entsprechenden Basisklassen- Subobjekt des Ausnahmeobjekts |
| CWG 2093 | C++98 |
ein Ausnahmeobjekt vom Zeiger-auf-Objekt-Typ konnte keinen
Handler vom Zeiger-auf-Objekt-Typ durch Qualifikationskonvertierung abgleichen |
erlaubt |
Referenzen
- C++23-Standard (ISO/IEC 14882:2024):
-
- 14.4 Behandlung einer Ausnahme [except.handle]
- C++20-Standard (ISO/IEC 14882:2020):
-
- 14.4 Behandlung einer Ausnahme [except.handle]
- C++17-Standard (ISO/IEC 14882:2017):
-
- 18.3 Behandlung einer Ausnahme [except.handle]
- C++14-Standard (ISO/IEC 14882:2014):
-
- 15.3 Behandlung einer Ausnahme [except.handle]
- C++11 Standard (ISO/IEC 14882:2011):
-
- 15.3 Behandlung einer Ausnahme [except.handle]
- C++03-Standard (ISO/IEC 14882:2003):
-
- 15.3 Behandlung einer Ausnahme [except.handle]
- C++98 Standard (ISO/IEC 14882:1998):
-
- 15.3 Behandlung einer Ausnahme [except.handle]