Notatka
Dostęp do tej strony wymaga autoryzacji. Może spróbować zalogować się lub zmienić katalogi.
Dostęp do tej strony wymaga autoryzacji. Możesz spróbować zmienić katalogi.
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ę
trynormalną sekwencyjną. Sekcja chroniona wtrybloku jest wykonywana.Jeśli podczas wykonywania chronionej sekcji nie jest zgłaszany żaden wyjątek, klauzule, które następują
catchpo bloku,trynie są wykonywane. Wykonanie jest kontynuowane w instrukcji po ostatniejcatchklauzuli, która jest zgodna ze skojarzonymtryblokiem.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
throwprzez operand. (Oznacza to, że może być zaangażowany konstruktor kopiujący). W tym momencie kompilator szukacatchklauzuli w wyższym kontekście wykonywania, który może obsługiwać wyjątek typu, który jest zgłaszany, lub dlacatchprogramu obsługi, który może obsłużyć dowolny typ wyjątku. Procedurycatchobsługi są badane w kolejności ich wyglądutrypo bloku. Jeśli nie zostanie znaleziona odpowiednia procedura obsługi, zostanie zbadany następny dynamicznie otaczającytryblok. 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
catchprocedura 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ątkiemtrybloku skojarzonego zcatchprocedurą obsługi a lokacją zgłaszania wyjątku. Destrukcja następuje w odwrotnej kolejności do konstrukcji. Proceduracatchobsługi jest wykonywana, a program wznawia wykonywanie po ostatniej procedurze obsługi — czyli w pierwszej instrukcji lub konstrukcji, która nie jest procedurącatchobsługi. Kontrolka może wprowadzać proceduręcatchobsługi tylko za pomocą wyjątku zgłaszanego, nigdy za pomocągotoinstrukcji lubcaseetykiety 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.
*/