Ausnahmen und Stapelentladung in C++
Im C++-Ausnahmemechanismus geht die Steuerung von der throw-Anweisung zur ersten catch-Anweisung, die den ausgelösten Typ behandeln kann. Wenn die Catch-Anweisung erreicht wird, werden alle automatischen Variablen, die sich im Bereich zwischen der Wurf- und Catch-Anweisung befinden, in einem Prozess zerstört, der als Stapelentspannung bezeichnet wird. Bei der Stapelentladung wird die Ausführung wie folgt fortgesetzt:
Control erreicht die
try
Anweisung durch normale sequenzielle Ausführung. Der geschützte Abschnitt imtry
Block wird ausgeführt.Wenn während der Ausführung des geschützten Abschnitts keine Ausnahme ausgelöst wird, werden die Klauseln, die
catch
demtry
Block folgen, nicht ausgeführt. Die Ausführung wird nach der letztencatch
Klausel, die dem zugeordnetentry
Block folgt, an der Anweisung fortgesetzt.Wenn eine Ausnahme während der Ausführung des geschützten Abschnitts oder in einer Routine ausgelöst wird, die der geschützte Abschnitt entweder direkt oder indirekt aufruft, wird ein Ausnahmeobjekt aus dem Objekt erstellt, das vom
throw
Operanden erstellt wird. (Dies bedeutet, dass ein Kopierkonstruktor beteiligt sein kann.) An diesem Punkt sucht der Compiler in einem Kontext mit höherer Ausführung nach einercatch
Klausel, die eine Ausnahme des ausgelösten Typs oder für einencatch
Handler verarbeiten kann, der einen beliebigen Ausnahmetyp verarbeiten kann. Diecatch
Handler werden nach demtry
Block in der Reihenfolge ihrer Darstellung untersucht. Wenn kein geeigneter Handler gefunden wird, wird der nächste dynamisch eingeschlossenetry
Block untersucht. Dieser Vorgang wird fortgesetzt, bis der äußerste umschließendetry
Block untersucht wird.Wenn ein entsprechender Handler weiterhin nicht gefunden wird oder wenn während des Entladungsprozesses eine Ausnahme auftritt, bevor der Handler die Steuerung übernimmt, wird die vordefinierte Laufzeitfunktion
terminate
aufgerufen. Wenn eine Ausnahme auftritt, nachdem die Ausnahme ausgelöst wurde, aber bevor die Abwicklung beginnt, wirdterminate
aufgerufen.Wenn ein übereinstimmenden
catch
Handler gefunden wird und nach Wert erfasst wird, wird der formale Parameter durch Kopieren des Ausnahmeobjekts initialisiert. Wenn er als Verweis abfängt, wird der Parameter so initialisiert, dass er auf das Ausnahmeobjekt verweist. Nachdem der formale Parameter initialisiert wurde, beginnt der Prozess der Stapelentladung. Dies umfasst die Zerstörung aller automatischen Objekte, die vollständig konstruiert wurden – aber noch nicht destruktiert – zwischen dem Anfang destry
Blocks, der demcatch
Handler zugeordnet ist, und der Auslösen-Site der Ausnahme. Beschädigung tritt in umgekehrter Reihenfolge der Konstruktion auf. Dercatch
Handler wird ausgeführt, und das Programm setzt die Ausführung nach dem letzten Handler fort , d. h. bei der ersten Anweisung oder dem Konstrukt, die keincatch
Handler ist. Steuerelement kann nur einencatch
Handler über eine ausgelöste Ausnahme eingeben, niemals durch einegoto
Anweisung oder einecase
Bezeichnung in einerswitch
Anweisung.
Beispiel für die Stapelentwickelung
Das folgende Beispiel zeigt, wie der Stapel entladen wird, wenn eine Ausnahme ausgelöst wird. Die Ausführung auf dem Thread springt von der throw-Anweisung in C
auf die catch-Anweisung in main
und entlädt währenddessen jede Funktionen. Beachten Sie die Reihenfolge, in der die Dummy
-Objekte erstellt und dann zerstört werden, wenn sie den Gültigkeitsbereich verlassen. Beachten Sie außerdem, dass keine Funktion außer main
abgeschlossen wird, die die catch-Anweisung enthält. Funktion A
kehrt nie von ihrem Aufruf von B()
zurück und B
nie von seinem Aufruf von C()
. Wenn Sie die Definition des Dummy
-Zeigers und die zugehörige Löschanweisung auskommentieren und das Programm dann ausführen, sehen Sie, dass der Zeiger nie gelöscht wird. Dies zeigt, was geschehen kann, wenn Funktionen keine Ausnahmegarantie bieten. Weitere Informationen finden Sie unter How to: Design for Exceptions. Wenn Sie die catch-Anweisung auskommentieren, können Sie nachvollziehen, was geschieht, wenn ein Programm aufgrund eines Ausnahmefehlers beendet wird.
#include <string>
#include <iostream>
using namespace std;
class MyException{};
class Dummy
{
public:
Dummy(string s) : MyName(s) { PrintMsg("Created Dummy:"); }
Dummy(const Dummy& other) : MyName(other.MyName){ PrintMsg("Copy created Dummy:"); }
~Dummy(){ PrintMsg("Destroyed Dummy:"); }
void PrintMsg(string s) { cout << s << MyName << endl; }
string MyName;
int level;
};
void C(Dummy d, int i)
{
cout << "Entering FunctionC" << endl;
d.MyName = " C";
throw MyException();
cout << "Exiting FunctionC" << endl;
}
void B(Dummy d, int i)
{
cout << "Entering FunctionB" << endl;
d.MyName = "B";
C(d, i + 1);
cout << "Exiting FunctionB" << endl;
}
void A(Dummy d, int i)
{
cout << "Entering FunctionA" << endl;
d.MyName = " A" ;
// Dummy* pd = new Dummy("new Dummy"); //Not exception safe!!!
B(d, i + 1);
// delete pd;
cout << "Exiting FunctionA" << endl;
}
int main()
{
cout << "Entering main" << endl;
try
{
Dummy d(" M");
A(d,1);
}
catch (MyException& e)
{
cout << "Caught an exception of type: " << typeid(e).name() << endl;
}
cout << "Exiting main." << endl;
char c;
cin >> c;
}
/* Output:
Entering main
Created Dummy: M
Copy created Dummy: M
Entering FunctionA
Copy created Dummy: A
Entering FunctionB
Copy created Dummy: B
Entering FunctionC
Destroyed Dummy: C
Destroyed Dummy: B
Destroyed Dummy: A
Destroyed Dummy: M
Caught an exception of type: class MyException
Exiting main.
*/
Feedback
https://aka.ms/ContentUserFeedback.
Bald verfügbar: Im Laufe des Jahres 2024 werden wir GitHub-Issues stufenweise als Feedbackmechanismus für Inhalte abbauen und durch ein neues Feedbacksystem ersetzen. Weitere Informationen finden Sie unterFeedback senden und anzeigen für