Obsługa wyjątków
beziak
Wyjątek - stan (zdarzenie wyjątkowe, w szczególności błąd), którego wystąpienie zmienia prawidłowy przebieg wykonywania się programu.
Składnia
Kod, który potencjalnie może zgłosić wyjątek umiejscawiamy w bloku try, zaś obsługę tych wyjątków w następujących zaraz po nim blokach catch: ```cpp try { // kod, w którym spodziewamy się rzucenia wyjątku } catch (KlasaWyjatku1& kl1) { // w razie rzucenia wyjątku klasy KlasaWyjatku1 wykonany zostanie ten kod } catch (KlasaWyjatku2& kl2) { // jak wyżej, ale gdy zostanie rzucony wyjątek klasy KlasaWyjatku2 } ```Zgłaszanie (rzucanie) wyjątków
Do zgłaszania wyjątków służy słowo kluczowe throw ```cpp throw wyjatek; ```Warto wiedzieć, że nie musimy w ten sposób rzucać obiektów klas. Równie dobrze możemy posłużyć się typem wbudowanym:
throw (unsigned long) 0xDEADBEEF;
Hierarchia wyjątków
Przesłanką przemawiającą za rzucaniem obiektów klas jest możliwość stworzenia hierarchii wyjątków. Wynika to stąd, że bloki catch są w stanie łapać obiekty swojej klasy lub klasy pochodnej. W celu zilustrowania tej właściwości przedstawmy następujący przykład (nazwy klas bardzo obrazowe):- Mamy klasę o nazwie Wypadek:
class Wypadek { // Treść nieistotna };
- Tworzymy klasy o nazwie WypadekKolejowy i WypadekSamochodowy dziedziczące z klasy Wypadek:
class WypadekKolejowy : public Wypadek { // ... }; class WypadekSamochodowy : public Wypadek { // ... };
- Dodatkowo definiujemy klasę Karambol dziedziczącą z klasy WypadekSamochodowy:
class Karambol : public WypadekSamochodowy { // ... };
Przykładowy kod przyjmijmy taki:
try
{
throw Karambol();
}
catch (Karambol& w)
{
std::cout << "Złapano obiekt >>Karambol<<\n";
}
catch (WypadekSamochodowy& w)
{
std::cout << "Złapano obiekt >>WypadekSamochodowy<<\n";
}
catch (Wypadek& w)
{
std::cout << "Złapano obiekt >>Wypadek<<\n";
}
catch (...)
{
std::cout << "Złapał catch(...)!\n";
}
W takim wypadku, jeżeli zgłosimy Karambol
throw Karambol();
wyjątek ten zostanie przechwycony przez blok:
catch (Karambol& w)
{
std::cout << "Złapano obiekt >>Karambol<<\n";
}
Jeżeli natomiast zgłosimy WypadekSamochodowy, zostanie on przechwycony przez drugi blok.
Powstaje teraz pytanie: co się stanie, jak w bloku try znajdzie się taki kod:
throw WypadekKolejowy();
Nie ma w naszym kodzie bloku catch odpowiedzialnego za łapanie tego typu obiektów. Wypadek kolejowy jest jednak szczególnym rodzajem wypadku - tak samo u nas WypadekKolejowy jest klasą pochodną od Wypadek. Wyjątek zostanie zatem przechwycony przez blok:
catch (Wypadek& w)
{
std::cout << "Złapano obiekt >>Wypadek<<\n";
}
Na takiej samej zasadzie, jeżeli usuniemy blok odpowiedzialny za łapanie Karambolu, łapać takie wyjątki będzie blok:
catch (WypadekSamochodowy& w)
{
std::cout << "Złapano obiekt >>WypadekSamochodowy<<\n";
}
Blok catch(...)
W przykładzie powyżej przemycono blok catch z wielokropkiem. Zadanie takiego bloku to: "cokolwiek by nie leciało, łap to!" - blok taki będzie wychwytywał wszystkie nieprzechwycone wcześniej wyjątki. Przykładowo, jeżeli w naszym bloku try rzucimy obiekt typu char: ```cpp throw 'Q'; ``` wyjątek ten zostanie przechwycony przez ostatni blok catch. Wadą takiego "uniwersalnego" bloku *catch* jest brak możliwości odwołania się do złapanego obiektu wyjątku (nie da się sprawdzić, co tak naprawdę "poleciało").Kolejność bloków catch
Kolejność łapania wyjątków nie jest dowolna. Próba skompilowania następującego kodu: catch (Wypadek& w) { }
catch (WypadekSamochodowy& w) { }
spowoduje zgłoszenie błędu kompilacji. Zasada definiowania bloków catch jest następująca:
Wyjątki należy przechwytywać zaczynając od najbardziej szczegółowych, a kończąc na najbardziej ogólnych.
Z powyższego wynika zatem, że blok catch(...) musi być zawsze ostatnim.
Uwagi
- Wyjątki można łapać zarówno poprzez wartość, jak i poprzez referencję, ze wszystkimi tego konsekwencjami. Niektórzy ortodoksyjni programiści C++ uważają łapanie wyjątków przez wartość za *obrzydliwe* ;)
- Bloki try...catch można zagnieżdżać. Nie stanowi to problemu, aby wewnątrz bloku try znalazła się cała konstrukcja try...catch.
- Niezłapane wyjątki powodują zakończenie wykonywania programu i wypisanie komunikatu o błędzie.
- Nie należy rzucać wyjątków z destruktorów! Zignorowanie tego faktu może powodować nieoczekiwane zakończenia wykonywania programu.
- Nie powinno się wyrzucać wyjątków z bloku catch(). Spowoduje to wywołanie funkcji terminate() i w konsekwencji zakończenie działania programu.
- Mechanizm wyjątków, jak sama nazwa wskazuje, jest używany do obsługi sytuacji *wyjątkowych*, przez co nie jest specjalnie optymalizowany pod kątem wydajności działania. Nie należy go nadużywać.
Jeszcze nie ma, że w bloku catch(...) nie ma możliwości sprawdzenia co poleciało.
najłatwiej to samemu nic nie robić, tylko komentować ;-) I jeszcze te cytaty, za miesiąc ktoś napisze artykuł od nowa i wszystko w komentarzach będzie po prostu bez sensu :-). Tego typu komenty można wysłać przez PW. Poza tym artykuł jest i tak lepszy niż poprzednia wersja.
Jeżeli w artykule o wyjątkach ani razu nie pojawił się wyraz "stos", tzn. że taki tekst jest kompletnie bezwartościowy. A niniejszy artykuł zawiera jeszcze bardzo rażące błędy, jak na przykład "Wyjątek - jest to specjalny obiekt". I jeszcze to obrzydliwe rzucanie wyjątków przez wartość zamiast przez referencję - masakra. Nawet szkoda czasu na poprawianie. Całość do kosza.
oj malutko, więc słabiutko