Namespaces
Variants

Handling exceptions

From cppreference.net
C++ language
General topics
Flow control
Conditional execution statements
Iteration statements (loops)
Jump statements
Functions
Function declaration
Lambda function expression
inline specifier
Dynamic exception specifications ( until C++17* )
noexcept specifier (C++11)
Exceptions

catch handler
Namespaces
Types
Specifiers
constexpr (C++11)
consteval (C++20)
constinit (C++20)
Storage duration specifiers
Initialization
Expressions
Alternative representations
Literals
Boolean - Integer - Floating-point
Character - String - nullptr (C++11)
User-defined (C++11)
Utilities
Attributes (C++11)
Types
typedef declaration
Type alias declaration (C++11)
Casts
Memory allocation
Classes
Class-specific function properties
Special member functions
Templates
Miscellaneous

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)
1) Ein Handler mit einem benannten Parameter.
2) Ein Handler mit einem unbenannten Parameter.
3) Ein Handler, der alle Arten von Ausnahmen abfängt.
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:

(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-qualifiziertes T ", und eine der folgenden Bedingungen ist erfüllt:
  • E und T sind derselbe Typ (unter Vernachlässigung der Top-Level-cv-Qualifizierer).
  • T ist eine eindeutige öffentliche Basisklasse von E .
  • Der Handler ist vom Typ „möglicherweise cv-qualifiziert T “ oder const T & , wobei T ein Zeiger- oder Zeiger-auf-Member-Typ ist und eine der folgenden Bedingungen erfüllt ist:
  • E ist ein Zeiger- oder Zeiger-auf-Mitglied-Typ, der durch mindestens eine der folgenden Konvertierungen in T umgewandelt werden kann:
(seit C++17)
(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 T eine Basisklasse von E ist, wird der Parameter copy-initialized von einem Lvalue des Typs T , der das entsprechende Basisklassen-Subobjekt des Exception-Objekts bezeichnet.
  • Andernfalls wird der Parameter von einem Lvalue des Typs E copy-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.

  • throw nullptr kann verwendet werden, um einen Nullzeiger zu werfen, der solchen Handlern entspricht.
(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

catch

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]

Siehe auch