Mądre przerzucanie wyjątków

Zdarza się niekiedy, że chcemy złapać wyjątek, zrobić coś (czy to coś posprzątać, czy to dopisać do wyjątku jakąś dodatkową informację) i puścić wyjątek dalej. Programista dziwił się, czemu w poniższym kodzie zewnętrzna para try/catch łapie wyjątek typu A a nie B.

#include <iostream.h>

class A {
public:
     A() {}
};

class B : public A {
public:
     B() {}
};

int main() {
   try {
       try {
           throw B();
       }
       catch (A& x) {
           cerr << "first catch " << endl;
           throw x;
       }
   }
   catch (B& x) {
       cerr << "B" << endl;
   }
   catch (A& x) {
       cerr << "A" << endl;
   }
   return 0;
}

Wytłumaczenie jest proste. Zgodnie ze standardem - i niezgodnie z tym, czego chciał programista, sekwencja

       catch (A& x) {
           cerr << "first catch " << endl;
           throw x;

powoduje utworzenie nowego obiektu typu A (przy pomocy konstruktora kopiującego, który dostaje jako parametr x) a następnie rzucenie dalej tego nowego obiektu. Nowy obiekt nic nie wie o tym, że był kopiowany z obiektu klasy B. Zgubiliśmy informację o typie i wszystkie informacje zawarte w obiekcie klasy B.

Poprawny zapis jest bardzo podobny:

       catch (A& x) {
           cerr << "first catch " << endl;
           throw;

(throw bez parametrów rzuca dalej obsługiwany aktualnie wyjątek). A pisać o tym warto choćby dlatego, że w przeciągu roku nadziały się na to cztery różne znane mi osoby.

komentarze obsługiwane przez Disqus