Programowanie w języku C/C++

Obsługa wyjątków

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:
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
 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:
 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ć.

7 komentarzy

winerfresh 2009-04-02 18:19

Jeszcze nie ma, że w bloku catch(...) nie ma możliwości sprawdzenia co poleciało.

manfredek 2009-03-27 15:45

<quote=Mammoth>I jeszcze te cytaty, za miesiąc ktoś napisze artykuł od nowa i wszystko w komentarzach będzie po prostu bez sensu :-).</quote>O rly? O czymś takim jak 'Historia' słyszał?

Mammoth 2009-03-27 10:33

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.

manfredek 2009-03-25 16:00

<quote>rzucanie wyjątków przez wartość zamiast przez referencję</quote>Chyba łapanie?

Intelli 2009-03-25 13:45

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.

manfredek 2009-03-23 15:33

<quote=programik3>2.Przestrzegaj kolejności sprawdzania wyjątków</quote>WTF?

crayze 2009-03-23 08:59

oj malutko, więc słabiutko