Namensräume
Varianten
Aktionen

Ausnahmen behandeln

Von cppreference.com
< cpp‎ | Sprache
 
 
C++ Sprache
Allgemeine Themen
Kontrollfluss
Bedingte Ausführungsaussagen
if
Iterationsanweisungen (Schleifen)
for
Bereichs-for (C++11)
Sprunganweisungen
Funktionen
Funktionsdeklaration
Lambda-Funktionsausdruck
inline-Spezifizierer
Dynamische Ausnahmespezifikationen (bis C++17*)
noexcept-Spezifizierer (C++11)
Ausnahmen

catch-Handler
Namensräume
Typen
Spezifizierer
const/volatile
decltype (C++11)
auto (C++11)
constexpr (C++11)
consteval (C++20)
constinit (C++20)
Speicherdauer-Spezifizierer
Initialisierung
Ausdrücke
Alternative Darstellungen
Literale
Boolesch - Ganzzahl - Gleitkommazahl
Zeichen - String - nullptr (C++11)
Benutzerdefinierte (C++11)
Dienstprogramme
Attribute (C++11)
Typen
typedef-Deklaration
Typalias-Deklaration (C++11)
Umwandlungen
Speicherzuweisung
Klassen
Klassenspezifische Funktionseigenschaften
explicit (C++11)
static

Spezielle Member-Funktionen
Templates
Sonstiges
 
 
Ausnahmen
try Block
Ausnahmen auslösen
Ausnahmen behandeln
Ausnahmespezifikation
    noexcept Spezifikation (C++11)
    dynamische Spezifikation (bis C++17*)
noexcept Operator (C++11)
 

Eine Ausnahme kann von einem Handler behandelt werden.

Inhalt

[bearbeiten] 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 benanntem Parameter.
2) Ein Handler mit unbenanntem Parameter.
3) Ein Handler, der alle Arten von Ausnahmen abgleicht.
attr - (seit C++11) eine beliebige Anzahl von Attributen, die auf den Parameter angewendet werden
type-specifier-seq - Teil einer formalen Parameterdeklaration, wie in einer Parameterliste einer Funktion
Deklarator - Teil einer Parameterdeklaration, wie in einer Parameterliste einer Funktion
abstract-declarator - Teil einer unbenannten Parameterdeklaration, wie in einer Parameterliste einer Funktion
compound-statement - eine zusammengesetzte Anweisung


Die Parameterdeklaration in einem Handler beschreibt den oder die Typen von Ausnahmen, die dazu führen können, dass dieser Handler betreten wird.

Wenn der Parameter mit einem der folgenden Typen deklariert ist, ist das Programm schlecht geformt

(seit C++11)
  • ein Zeiger auf einen unvollständigen Typ, außer (möglicherweise cv-qualifiziertem) void
  • eine lvalue-Referenz auf einen unvollständigen Typ

Wenn der Parameter vom Typ „Array von T“ oder vom Funktionstyp T deklariert ist, wird der Typ zu „Zeiger auf T“ angepasst.

Ein Handler mit dem Parametertyp T kann als „ein Handler vom Typ T“ abgekürzt werden.

[bearbeiten] Übereinstimmende Ausnahmen

Jeder try-Block ist mit einer Anzahl von Handlern assoziiert; diese Handler bilden eine Handlersequenz. Wenn eine Ausnahme aus einem try-Block geworfen wird, werden die Handler in der Reihenfolge ihres Erscheinens versucht, um die Ausnahme abzugleichen.

Ein Handler ist eine Übereinstimmung für ein Ausnahmeobjekt 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 (oberste cv-Qualifizierer ignoriert).
  • T ist eine eindeutige öffentliche Basisklasse von E.
  • Der Handler ist vom Typ „möglicherweise cv-qualifiziertes T“ oder const T&, wobei T ein Zeiger- oder Zeiger-auf-Mitglied-Typ ist, und eine der folgenden Bedingungen ist erfüllt
  • E ist ein Zeiger- oder Zeiger-auf-Mitglied-Typ, der durch mindestens eine der folgenden Konvertierungen in T konvertiert werden kann
(seit C++17)
(seit C++11)

Der catch (...)-Handler gleicht Ausnahmen beliebigen Typs ab. Falls vorhanden, kann er nur der letzte Handler in einer Handlersequenz sein. Dieser Handler kann verwendet werden, um sicherzustellen, dass keine unbehandelten Ausnahmen aus einer Funktion entkommen können, die eine Nothrow-Ausnahmegarantie bietet.

try
{
    f();
}
catch (const std::overflow_error& e)
{} // this executes if f() throws std::overflow_error (same type rule)
catch (const std::runtime_error& e)
{} // this executes if f() throws std::underflow_error (base class rule)
catch (const std::exception& e)
{} // this executes if f() throws std::logic_error (base class rule)
catch (...)
{} // this executes if f() throws std::string or int or any other unrelated type

Wenn keine Übereinstimmung unter den Handlern für einen try-Block gefunden wird, wird die Suche nach einem übereinstimmenden Handler in einem dynamisch umschließenden try-Block fortgesetzt desselben Threads(seit C++11).

Wenn kein übereinstimmender Handler gefunden wird, wird std::terminate aufgerufen; ob der Stack vor diesem Aufruf von std::terminate entrollt wird, ist implementierungsabhängig.

[bearbeiten] Ausnahmen behandeln

Wenn eine Ausnahme geworfen wird, wird die Kontrolle an den nächstgelegenen Handler mit einem übereinstimmenden Typ übertragen; „nächstgelegen“ bedeutet der Handler, dessen zusammengesetzte Anweisung oder Member-Initialisierungsliste (falls vorhanden) nach dem Schlüsselwort try vom Kontrollfaden am kürzesten betreten und noch nicht verlassen wurde.

[bearbeiten] Initialisierung des Handler-Parameters

Der Parameter, der in der Parameterliste deklariert ist (falls vorhanden), vom Typ „möglicherweise cv-qualifiziertes T“ oder „lvalue-Referenz auf möglicherweise cv-qualifiziertes T“, wird vom Ausnahmeobjekt vom Typ E wie folgt initialisiert

  • Wenn T eine Basisklasse von E ist, wird der Parameter von einem lvalue vom Typ T, der die entsprechende Basisklassen-Subobjekt des Ausnahmeobjekts bezeichnet, kopierinitialisiert.
  • Andernfalls wird der Parameter von einem lvalue vom Typ E, der das Ausnahmeobjekt bezeichnet, kopierinitialisiert.

Die Lebensdauer des Parameters endet, wenn der Handler verlassen wird, nach der Zerstörung aller Objekte mit automatischer Speicherdauer, die innerhalb des Handlers initialisiert wurden.

Wenn der Parameter als Objekt deklariert ist, haben Änderungen an diesem Objekt keine Auswirkungen auf das Ausnahmeobjekt.

Wenn der Parameter als Referenz auf ein Objekt deklariert ist, sind Änderungen am referenzierten Objekt Änderungen am Ausnahmeobjekt und haben Auswirkungen, falls dieses Objekt erneut geworfen wird.

[bearbeiten] Aktivierung des Handlers

Ein Handler gilt als aktiv, wenn die Initialisierung für den Parameter (falls vorhanden) des Handlers abgeschlossen ist.

Außerdem gilt ein impliziter Handler als aktiv, wenn std::terminate aufgrund eines `throw` betreten wird.

Ein Handler gilt nicht mehr als aktiv, wenn der Handler verlassen wird.

Die Ausnahme mit dem zuletzt aktivierten Handler, der noch aktiv ist, wird als aktuell behandelte Ausnahme bezeichnet. Eine solche Ausnahme kann erneut geworfen werden.

[bearbeiten] Kontrollfluss

Die compound-statement eines Handlers ist eine kontrollflussbeschränkte Anweisung

void f()
{
    goto label;     // error
    try
    {
        goto label; // error
    }
    catch (...)
    {
        goto label: // OK
        label: ;
    }
}

[bearbeiten] Hinweise

Stack unwinding tritt auf, während die Kontrolle an einen Handler übertragen wird. Wenn ein Handler aktiv wird, ist das Stack unwinding bereits abgeschlossen.

Die von dem `throw`-Ausdruck throw 0 geworfene Ausnahme stimmt nicht mit einem Handler vom Zeiger- oder Zeiger-auf-Mitglied-Typ überein.

  • throw nullptr kann stattdessen verwendet werden, um einen Nullzeiger zu werfen, der mit solchen Handlern übereinstimmt.
(seit C++11)

Ausnahmeobjekte können niemals Array- oder Funktionstypen haben, daher ist ein Handler, der eine Referenz auf einen Array- oder Funktionstyp ist, niemals eine Übereinstimmung für irgendein Ausnahmeobjekt.

Es ist möglich, Handler zu schreiben, die niemals ausgeführt werden können, z. B. indem ein Handler für eine finale abgeleitete Klasse nach einem Handler für eine entsprechende eindeutige öffentliche Basisklasse platziert wird

try
{
    f();
}
catch (const std::exception& e)
{} // will be executed if f() throws std::runtime_error
catch (const std::runtime_error& e)
{} // dead code!

Viele Implementierungen erweitern übermäßig die Auflösung von CWG-Problem 388 auf Handler für Referenzen auf nicht-const Zeigertypen

int i;
try
{
    try
    {
        throw static_cast<float*>(nullptr);
    }
    catch (void*& pv)
    {
        pv = &i;
        throw;
    }
}
catch (const float* pf)
{
    assert(pf == nullptr); // should pass, but fails on MSVC and Clang
}

[bearbeiten] Schlüsselwörter

catch

[bearbeiten] Beispiel

Das folgende Beispiel demonstriert verschiedene Anwendungsfälle von Handlern

#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'

[bearbeiten] Fehlerberichte

Die folgenden Verhaltensändernden Fehlerberichte wurden rückwirkend auf zuvor veröffentlichte C++-Standards angewendet.

DR angewendet auf Verhalten wie veröffentlicht Korrigiertes Verhalten
CWG 98 C++98 Eine switch-Anweisung kann die Kontrolle in einen Handler übertragen verboten
CWG 210 C++98 throw-Ausdrücke wurden mit den Handlern abgeglichen Ausnahmeobjekte sind
mit den Handlern abgeglichen
CWG 388 C++98 Ein Ausnahmeobjekt vom Typ Zeiger oder Zeiger auf Mitglied konnte
nicht durch eine const-Referenz auf einen anderen Typ abgeglichen werden
wurde abgleichbar gemacht
wenn konvertierbar
CWG 1166 C++98 Das Verhalten war undefiniert, wenn ein Handler, dessen
Typ eine Referenz auf einen abstrakten Klassentyp ist, abgeglichen wurde
abstrakte Klassentypen sind
nicht für Handler erlaubt
CWG 1769 C++98 Wenn der Typ des Handlers eine Basisklasse des Typs des
Ausnahmeobjekts ist, könnte ein konvertierender Konstruktor
für die Initialisierung des Handler-Parameters verwendet werden
Der Parameter wird kopierinitialisiert
aus der entsprechenden Basisklasse
Subobjekt des Ausnahmeobjekts
CWG 2093 C++98 Ein Ausnahmeobjekt vom Typ Zeiger auf Objekt konnte keinen
Handler vom Typ Zeiger auf Objekt durch eine Qualifizierungskonvertierung abgleichen
erlaubt

[bearbeiten] Referenzen

  • C++23 Standard (ISO/IEC 14882:2024)
  • 14.4 Handling an exception [except.handle]
  • C++20 Standard (ISO/IEC 14882:2020)
  • 14.4 Handling an exception [except.handle]
  • C++17 Standard (ISO/IEC 14882:2017)
  • 18.3 Handling an exception [except.handle]
  • C++14 Standard (ISO/IEC 14882:2014)
  • 15.3 Handling an exception [except.handle]
  • C++11 Standard (ISO/IEC 14882:2011)
  • 15.3 Handling an exception [except.handle]
  • C++03-Standard (ISO/IEC 14882:2003)
  • 15.3 Handling an exception [except.handle]
  • C++98 Standard (ISO/IEC 14882:1998)
  • 15.3 Handling an exception [except.handle]

[bearbeiten] Siehe auch