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.
- «Ostrożnie z konstruktorem std::string
- Nie używaj unsigned jako indeksu string-a»
- ↑C++ - sztuczki i niebezpieczeństwa