Wyjątki i odwijanie stosu w języku C++
W mechanizmie wyjątków C++, kontrola zostaje przekazana z instrukcji throw do pierwszej instrukcji catch, która może obsłużyć wyrzucony typ. Po osiągnięciu instrukcji catch wszystkie zmienne automatyczne, które znajdują się w zakresie między instrukcjami throw i catch, są niszczone w procesie znanym jako odwijanie stosu. W odwijaniu stosu, wykonanie przebiega w następujący sposób:
Kontrolka osiąga instrukcję
try
normalną sekwencyjną. Sekcja chroniona wtry
bloku jest wykonywana.Jeśli podczas wykonywania chronionej sekcji nie jest zgłaszany żaden wyjątek, klauzule, które następują
try
po bloku,catch
nie są wykonywane. Wykonanie jest kontynuowane w instrukcji po ostatniejcatch
klauzuli, która jest zgodna ze skojarzonymtry
blokiem.Jeśli podczas wykonywania chronionej sekcji jest zgłaszany wyjątek lub w jakiejkolwiek procedurze wywoływanej bezpośrednio lub pośrednio przez chronioną sekcję, obiekt wyjątku jest tworzony na podstawie obiektu utworzonego
throw
przez operand. (Oznacza to, że może być zaangażowany konstruktor kopiujący). W tym momencie kompilator szukacatch
klauzuli w wyższym kontekście wykonywania, który może obsługiwać wyjątek typu, który jest zgłaszany, lub dlacatch
programu obsługi, który może obsłużyć dowolny typ wyjątku. Procedurycatch
obsługi są badane w kolejności ich wyglądutry
po bloku. Jeśli nie zostanie znaleziona odpowiednia procedura obsługi, zostanie zbadany następny dynamicznie otaczającytry
blok. Ten proces trwa do czasu zbadania najbardziej zewnętrznego bloku otaczającegotry
.Jeśli nadal nie znaleziono pasującej klauzuli obsługi lub jeśli podczas procesu odwijania wystąpi wyjątek, ale zanim klauzula obsługi przejmie kontrolę, wywoływana jest wstępnie zdefiniowana funkcja
terminate
. Jeśli wyjątek wystąpi po wyrzuceniu wyjątku, ale przed rozpoczęciem odwijania, wywoływane jestterminate
.Jeśli zostanie znaleziona zgodna
catch
procedura obsługi i przechwytuje ją według wartości, jego parametr formalny jest inicjowany przez skopiowanie obiektu wyjątku. Jeżeli przechwyci kontrolę przez odwołanie, inicjowany jest parametr odwołujący się do obiektu wyjątku. Po zainicjowaniu parametru formalnego, rozpocznie się proces odwijania stosu. Obejmuje to zniszczenie wszystkich obiektów automatycznych, które zostały w pełni skonstruowane — ale jeszcze niezniszczone — między początkiemtry
bloku skojarzonego zcatch
procedurą obsługi a lokacją zgłaszania wyjątku. Destrukcja następuje w odwrotnej kolejności do konstrukcji. Proceduracatch
obsługi jest wykonywana, a program wznawia wykonywanie po ostatniej procedurze obsługi — czyli w pierwszej instrukcji lub konstrukcji, która nie jest procedurącatch
obsługi. Kontrolka może wprowadzać proceduręcatch
obsługi tylko za pomocą wyjątku zgłaszanego, nigdy za pomocągoto
instrukcji lubcase
etykiety w instrukcjiswitch
.
Przykład odwijania stosu
W poniższym przykładzie zilustrowano, jak stos jest odwijany, gdy zostaje wyrzucony wyjątek. Wykonanie na wątku przechodzi z instrukcji throw w C
do instrukcji catch w main
i rozwija każdą funkcję po drodze. Należy zauważyć kolejność, w której obiekty Dummy
są tworzone i następnie niszczone, gdy wykraczają poza zakres. Należy również zauważyć, że żadna funkcja nie zostaje zakończona z wyjątkiem main
, która zawiera instrukcję catch. Funkcja A
nigdy nie wraca z wywołania B()
, a B
nigdy nie wraca z wywołania C()
. Jeśli usuniesz komentarz definicji wskaźnika Dummy
i odpowiadającą instrukcję delete, a następnie uruchomisz program, zobaczysz, że wskaźnik nigdy nie jest usuwany. Pokazuje to, co może się zdarzyć, gdy funkcje nie zapewniają gwarancji wyjątku. Aby uzyskać więcej informacji, zobacz How to: Design for Exceptions (Instrukcje: projektowanie wyjątków). Jeśli komentarz wystąpi poza instrukcją catch, można zaobserwować, co się dzieje, gdy program zakończy wykonanie z powodu nieobsługiwanego wyjątku.
#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.
*/
Opinia
https://aka.ms/ContentUserFeedback.
Dostępne już wkrótce: W 2024 r. będziemy stopniowo wycofywać zgłoszenia z serwisu GitHub jako mechanizm przesyłania opinii na temat zawartości i zastępować go nowym systemem opinii. Aby uzyskać więcej informacji, sprawdź:Prześlij i wyświetl opinię dla