C++/Obsługa wyjątków
Z Wikibooks, biblioteki wolnych podręczników.
Spis treści |
[edytuj] Wstęp
Wyjątki pozwalają reagować na różne sytuacje wyjątkowe. Używa się ich tam gdzie istnieje ryzyko wystąpienia wyjątku.
[edytuj] Zarys wyjątków
Jeżeli w jakimś miejscu programu zajdzie nieoczekiwana sytuacja, programista piszący ten kod powinien zasygnalizować o tym. Dawniej polegało to na zwróceniu specyficznej wartości, co nie było zbyt szczęśliwym rozwiązaniem, bo sygnał musiał być taki jak wartość zwracana przez funkcję. W przypadku obsługi sytuacji wyjątkowej mówi się o obiekcie sytuacji wyjątkowej, co często zastępowane jest słowem "wyjątek". W C++ wyjątki się "rzuca", służy do tego instrukcja throw.
[edytuj] Szkielet obsługi wyjątków
Tam gdzie spodziewamy się wyjątku umieszczamy blok try, w którym umieszczamy "podejrzane" instrukcje. Za tym blokiem muszą (tzn. musi przynajmniej jedna) pojawić się bloki catch. Wygląda to tak:
//jakaś zwykła funkcja, lub funkcja main
try // w instrukcjach poniżej może coś się nie udać
{
fun();
fun2(); //podejrzane funkcje
}
catch(char obj)
{
//tu coś robimy, na przykład piszemy o błędzie
}
W instrukcji catch umieszczamy typ jakim będzie wyjątek. Rzucić możemy wyjątek typu int, char i inne, dlatego tu określamy co nas interesuje. Nazwa tego obiektu nie jest konieczna, ale jeżeli chcemy znać wartość musimy ten obiekt nazwać. Bloków catch może być więcej, najczęściej tyle ile możliwych typów do złapania. Co ważne jeżeli rzucimy wyjątek konkretnego typu to "wpadnie" on do pierwszego dobrego catch nawet jeżeli inne nadają się lepiej (podobnie jak z instrukcjami if else). Dotyczy to zwłaszcza klas dziedziczonych. Przykładowo, jeżeli mamy klasę Pies, która dziedziczy z klasy Zwierze, to jeśli pierwszy pojawi się blok:
catch(Zwierze obj)
to on zostanie użyty do obsługi wyjątku.
Zawsze dobrze jest się zabezpieczyć blokiem
catch(...)
Taki blok łapie wszystko. Dlatego kompilator nie dopuści by wszystko łapiący catch był przed innymi instrukcjami catch.
[edytuj] Rzucanie wyjątku
Pisząc funkcję możemy stwierdzić że coś poszło nie tak i chcemy zasygnalizować wyjątek. Jak to zrobic przedstawia kod:
double Dziel(double a, double b) //funkcja zwraca iloraz a / b
{
if(b == 0) //przez zero się nie dzieli
throw "dzielenie przez zero!"; //rzucamy wyjątek
return a / b;
}
Po instrukcji throw umieszczamy obiekt który chcemy rzucić (u nas jest to char*). W tym miejscu działanie funkcji jest natychmiast przerywane i nasz łańcuch znaków wędruje do bloków catch.
Pełny program ilustrujący wyjątki:
#include<iostream>
using namespace std;
double Dziel(double a, double b) //funkcja zwraca iloraz a / b
{
if(b == 0) //przez zero się nie dzieli
throw "dzielenie przez zero!"; //rzucamy wyjątek
return a / b;
}
int main()
{
try
{
Dziel(10, 0);
}
catch(const char* w)
{
cout<<"Wyjatek: "<<w;
}
cin.get();
return 0;
}
Jak widać, utworzony jest tylko jeden blok catch, a to dlatego że funkcja Dziel rzuca tylko wyjątki typu const char*. Dobrym zwyczajem jest pisanie obok deklaracji funkcji jakie wyjątki może ona rzucać:
void fun(int aa) throw(char)
zapis ten oznacza że funkcja fun może zwrócić wyjątek typu char.