Namespaces
Variants

Throwing 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
throw -expression
try block
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

Das Werfen einer Exception übergibt die Kontrolle an einen Handler .

Eine Ausnahme kann aus throw expressions geworfen werden, die folgenden Kontexte können ebenfalls Ausnahmen werfen:

Inhaltsverzeichnis

Exception-Objekt

Das Werfen einer Exception initialisiert ein Objekt mit dynamischer storage duration , genannt das exception object .

Wenn der Typ des Ausnahmeobjekts einer der folgenden Typen wäre, ist das Programm fehlerhaft:

Konstruktion und Destruktion von Exception-Objekten

Angesichts des Typs des Ausnahmeobjekts als T :

  • Sei obj ein L-Wert vom Typ const T , dann muss die Copy-Initialisierung eines Objekts vom Typ T aus obj wohlgeformt sein.
  • Falls T ein Klassentyp ist:

Der Speicher für das Ausnahmeobjekt wird auf nicht näher spezifizierte Weise allokiert. Die einzige Garantie ist, dass der Speicher niemals durch globale Allokationsfunktionen bereitgestellt wird.

Wenn ein Handler durch erneutes Werfen beendet wird, wird die Steuerung an einen anderen Handler für dasselbe Ausnahmeobjekt übergeben. Das Ausnahmeobjekt wird in diesem Fall nicht zerstört.

Wenn der letzte verbleibende aktive Handler für die Ausnahme auf eine andere Weise als durch erneutes Werfen beendet wird, wird das Ausnahmeobjekt zerstört und die Implementierung kann den Speicher für das temporäre Objekt auf nicht spezifizierte Weise freigeben.

Die Zerstörung erfolgt unmittelbar nach der Zerstörung des im "Parameterlist" des Handlers deklarierten Objekts.

(bis C++11)

Die möglichen Zeitpunkte für die Zerstörung des Ausnahmeobjekts sind:

  • Wenn ein aktiver Handler für die Ausnahme auf eine andere Weise als durch erneutes Werfen beendet wird, unmittelbar nach der Zerstörung des (falls vorhanden) im "Parameterlist" des Handlers deklarierten Objekts.
  • Wenn ein Objekt vom Typ std::exception_ptr , das auf das Ausnahmeobjekt verweist, zerstört wird, bevor der Destruktor von std::exception_ptr zurückkehrt.

Unter allen möglichen Zeitpunkten für die Zerstörung des Ausnahmeobjekts gibt es einen nicht spezifizierten letzten Zeitpunkt, an dem das Ausnahmeobjekt zerstört wird. Alle anderen Zeitpunkte happen before diesem letzten Zeitpunkt. Die Implementierung kann dann den Speicher für das Ausnahmeobjekt auf nicht spezifizierte Weise freigeben.

(seit C++11)

throw Ausdrücke

throw Ausdruck (1)
throw (2)
1) Wirft eine neue Exception.
2) Wirft die aktuell behandelte Ausnahme erneut.
expression - der Ausdruck, der zur Konstruktion des Exception-Objekts verwendet wird


Wenn eine neue Exception ausgelöst wird, wird ihr Exception-Objekt wie folgt bestimmt:

  1. Die Array-zu-Zeiger und Funktion-zu-Zeiger Standardkonvertierungen werden auf den Ausdruck angewendet.
  2. Sei ex das Konvertierungsergebnis:
  • Der Typ des Ausnahmeobjekts wird durch Entfernen aller obersten cv-Qualifizierer vom Typ von ex bestimmt.
  • Das Ausnahmeobjekt wird copy-initialized von ex .

Wenn ein Programm versucht, eine Ausnahme erneut auszulösen, wenn gegenwärtig keine Ausnahme behandelt wird, std::terminate wird aufgerufen. Andernfalls wird die Ausnahme mit dem bestehenden Ausnahmeobjekt reaktiviert (es wird kein neues Ausnahmeobjekt erstellt), und die Ausnahme wird nicht länger als abgefangen betrachtet.

try
{
    // Auslösen einer neuen Ausnahme 123
    throw 123;
}
catch (...) // alle Ausnahmen abfangen
{
    // (Teilweise) auf Ausnahme 123 reagieren
    throw; // Ausnahme an anderen Handler weiterleiten
}

Stack-Abbau

Sobald das Ausnahmeobjekt konstruiert ist, arbeitet der Kontrollfluss rückwärts (den Aufrufstapel hinauf), bis er den Beginn eines try Blocks erreicht, an welchem Punkt die Parameter aller zugehörigen Handler der Reihe nach mit dem Typ des Ausnahmeobjekts verglichen werden, um eine Übereinstimmung zu finden. Wenn keine Übereinstimmung gefunden wird, setzt der Kontrollfluss das Abwickeln des Stapels fort bis zum nächsten try Block, und so weiter. Wenn eine Übereinstimmung gefunden wird, springt der Kontrollfluss zum passenden Handler.

Während der Kontrollfluss den Aufrufstapel hinaufsteigt, werden Destruktoren für alle Objekte mit automatischer Speicherdauer aufgerufen, die konstruiert, aber noch nicht zerstört wurden, seit der entsprechende try -Block betreten wurde, in umgekehrter Reihenfolge der Vollendung ihrer Konstruktoren. Wenn eine Exception aus einem Destruktor einer lokalen Variable oder eines temporären Objekts geworfen wird, das in einer return -Anweisung verwendet wird, wird auch der Destruktor für das aus der Funktion zurückgegebene Objekt aufgerufen.

Wenn eine Ausnahme aus einem Konstruktor oder (selten) aus einem Destruktor eines Objekts geworfen wird (unabhängig von der Speicherdauer des Objekts), werden Destruktoren für alle vollständig konstruierten nicht-statischen nicht-varianten Member und Basisklassen in umgekehrter Reihenfolge der Fertigstellung ihrer Konstruktoren aufgerufen. Variante Member von union-like classes werden nur im Falle des Unwindings aus dem Konstruktor zerstört, und wenn sich der aktive Member zwischen Initialisierung und Zerstörung ändert, ist das Verhalten undefiniert.

Wenn ein delegierender Konstruktor mit einer Exception beendet wird, nachdem der nicht-delegierende Konstruktor erfolgreich abgeschlossen wurde, wird der Destruktor für dieses Objekt aufgerufen.

(since C++11)

Wenn die Ausnahme von einem Konstruktor ausgelöst wird, der durch einen new-expression aufgerufen wird, wird die passende deallocation function aufgerufen, sofern verfügbar.

Dieser Vorgang wird als stack unwinding bezeichnet.

Wenn irgendeine Funktion, die direkt vom Stack-Unwinding-Mechanismus aufgerufen wird, nach der Initialisierung des Exception-Objekts und vor dem Beginn des Exception-Handlers mit einer Exception beendet wird, std::terminate wird aufgerufen. Solche Funktionen umfassen Destruktoren von Objekten mit automatischer Speicherdauer, deren Gültigkeitsbereiche verlassen werden, und den Kopierkonstruktor des Exception-Objekts, der (falls nicht elided ) aufgerufen wird, um Catch-by-Value-Argumente zu initialisieren.

Wenn eine Exception ausgelöst wird und nicht abgefangen wird, einschließlich Exceptions, die aus der initialen Funktion von std::thread , der main-Funktion und dem Konstruktor oder Destruktor von statischen oder thread-lokalen Objekten entweichen, wird std::terminate aufgerufen. Es ist implementierungsdefiniert, ob für nicht abgefangene Exceptions ein Stack Unwinding stattfindet.

Hinweise

Beim erneuten Auslösen von Ausnahmen muss die zweite Form verwendet werden, um Objektslicing im (typischen) Fall zu vermeiden, in dem Ausnahmeobjekte Vererbung verwenden:

try
{
    std::string("abc").substr(10); // wirft std::out_of_range
}
catch (const std::exception& e)
{
    std::cout << e.what() << '\n';
//  throw e; // initialisiert ein neues Exception-Objekt vom Typ std::exception durch Kopie
    throw;   // wirft das Exception-Objekt vom Typ std::out_of_range erneut
}

Der throw -Ausdruck wird klassifiziert als Prvalue-Ausdruck vom Typ void . Wie jeder andere Ausdruck kann er ein Teilausdruck in einem anderen Ausdruck sein, am häufigsten im Bedingungsoperator :

double f(double d)
{
    return d > 1e7 ? throw std::overflow_error("zu groß") : d;
}
int main()
{
    try
    {
        std::cout << f(1e10) << '\n';
    }
    catch (const std::overflow_error& e)
    {
        std::cout << e.what() << '\n';
    }
}
**Übersetzte Änderungen:** - `"too big"` → `"zu groß"` (die einzige zu übersetzende Textzeichenkette außerhalb der Code-Tags) Alle HTML-Tags, Attribute und der C++-Code innerhalb der `
`-Tags bleiben unverändert, wie angefordert.

Schlüsselwörter

throw

Beispiel

#include <iostream>
#include <stdexcept>
struct A
{
    int n;
    A(int n = 0): n(n) { std::cout << "A(" << n << ") constructed successfully\n"; }
    ~A() { std::cout << "A(" << n << ") destroyed\n"; }
};
int foo()
{
    throw std::runtime_error("error");
}
struct B
{
    A a1, a2, a3;
    B() try : a1(1), a2(foo()), a3(3)
    {
        std::cout << "B constructed successfully\n";
    }
    catch(...)
    {
        std::cout << "B::B() exiting with exception\n";
    }
    ~B() { std::cout << "B destroyed\n"; }
};
struct C : A, B
{
    C() try
    {
        std::cout << "C::C() completed successfully\n";
    }
    catch(...)
    {
        std::cout << "C::C() exiting with exception\n";
    }
    ~C() { std::cout << "C destroyed\n"; }
};
int main () try
{
    // creates the A base subobject
    // creates the a1 member of B
    // fails to create the a2 member of B
    // unwinding destroys the a1 member of B
    // unwinding destroys the A base subobject
    C c;
}
catch (const std::exception& e)
{
    std::cout << "main() failed to create C with: " << e.what();
}

Ausgabe:

A(0) constructed successfully
A(1) constructed successfully
A(1) destroyed
B::B() exiting with exception
A(0) destroyed
C::C() exiting with exception
main() failed to create C with: error

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 499 C++98 Ein Array mit unbekannter Grenze konnte nicht geworfen werden, da
sein Typ unvollständig ist, aber ein Ausnahmeobjekt kann
vom zerfallenen Zeiger ohne Probleme erstellt werden
Wende die Typ-Vervollständigungs-
anforderung stattdessen auf das
Ausnahmeobjekt an
CWG 668 C++98 std::terminate wurde nicht aufgerufen, wenn eine Ausnahme
vom Destruktor eines lokalen nicht-automatischen Objekts geworfen wird
Rufe std::terminate
in diesem Fall auf
CWG 1863 C++11 Copy-Konstruktor war nicht erforderlich für move-only
Ausnahmeobjekte beim Werfen, aber Kopieren später erlaubt
Copy-Konstruktor erforderlich
CWG 1866 C++98 Variant-Mitglieder wurden beim Stack-Unwinding aus Konstruktor verworfen Variant-Mitglieder zerstört
CWG 2176 C++98 Wurf aus einem lokalen Variablen-Destruktor
könnte Rückgabewert-Destruktor überspringen
Funktionsrückgabewert
zum Unwinding hinzugefügt
CWG 2699 C++98 throw "EX" würde tatsächlich char * werfen statt const char * korrigiert
CWG 2711 C++98 Die Quelle der Copy-Initialisierung des
Ausnahmeobjekts war nicht spezifiziert
Copy-initialisiert
von expression
CWG 2775 C++98 Die Anforderung an die Copy-Initialisierung des Ausnahmeobjekts war unklar Klar gestellt
CWG 2854 C++98 Die Speicherdauer von Ausnahmeobjekten war unklar Klar gestellt
P1825R0 C++11 Implizites Move von Parametern war verboten in throw erlaubt

Referenzen

  • C++23-Standard (ISO/IEC 14882:2024):
  • 7.6.18 Auslösen einer Ausnahme [expr.throw]
  • 14.2 Auslösen einer Ausnahme [except.throw]
  • C++20-Standard (ISO/IEC 14882:2020):
  • 7.6.18 Auslösen einer Ausnahme [expr.throw]
  • 14.2 Auslösen einer Ausnahme [except.throw]
  • C++17-Standard (ISO/IEC 14882:2017):
  • 8.17 Auslösen einer Exception [expr.throw]
  • 18.1 Auslösen einer Exception [except.throw]
  • C++14-Standard (ISO/IEC 14882:2014):
  • 15.1 Auslösen einer Ausnahme [except.throw]
  • C++11-Standard (ISO/IEC 14882:2011):
  • 15.1 Auslösen einer Ausnahme [except.throw]
  • C++03-Standard (ISO/IEC 14882:2003):
  • 15.1 Auslösen einer Ausnahme [except.throw]
  • C++98 Standard (ISO/IEC 14882:1998):
  • 15.1 Auslösen einer Ausnahme [except.throw]

Siehe auch

(bis C++17)