Niebezpieczne wyjątki

tech • 285 słów • 2 minuty czytania

Ostatnio trochę marudziłem o rzucaniu wyjątków w konstruktorach, to dziś może wspomnę o destruktorach i konstruktorach kopiujących.

Każdy chyba wie, że nie należy rzucać wyjątków z destruktorów. To samo dotyczy konstruktora kopiującego obiektu wyjątku.

Problem z destruktorami jest to związany z obsługą wyjątku przez kompilator (czas miedzy rzuceniem wyjątku a złapaniem go przez odpowiedni catch), czyli rozwijaniem stosu przez kompilator i wywoływaniu destruktorów dla zmiennych lokalnych znajdujących się w zasięgu, którym zgłoszono sytuacje wyjątkową.

Czyli jak łatwo się domyślić, nie jest zalecane rzucenie wyjątku w destruktorze wykonywanym w czasie obsługi wyjątku przez kompilator, bo doprowadzi do katastrofy - w ogólnym przypadku program wywoła std::terminate().

Istnieje funkcja std::uncaught_exception(), która zwraca true, jeśli akurat znajdujemy się w trakcie obsługiwania wyjątku. Dzięki niej możemy się dowiedzieć czy np. destruktor został wywołany normalnie, czy z powodu wystąpienia sytuacji wyjątkowej.

Aczkolwiek nie jest to zbytnio zalecane. Trochę wiecej informacji na ten temat można znaleźć w GotW #47.

Odnośnie konstruktorów kopiujących obiektu wyjątku, problem związany jest z możliwością skopiowania “rzuconego” obiektu w czasie jego obsługi przez kompilator. Gdy w tym czasie copy ctor obiektu wyjątku zgłosiłby kolejny wyjątek, sprawa wyglądałaby tak jak w przypadku wyjątków w destruktorach - rzucony wyjątek w czasie obsługi innego wyjątku - terminate().

Dlatego nie należy traktować dowolnej klasy jako obiektu wyjątku. Należy odpowiednio zadbać o zabezpieczenia klas, których zastosowanie przewidujemy do wyjątków. Klasa, która rzuca wyjątkami w konstruktorach, a tym szczególnie w kopiującym, w ogóle nie nadaje się do wyjątków.

Jako przykład weźmy std::exception.

Dlaczego w implementacji standardowych klas wyjątków zastosowano char* zamiast std::string?

Ponieważ konstruktor kopiujący std::string może rzucić wyjątkiem std::bad_alloc. Dlatego unika się w implementacjach klas wyjątków wykorzystywanie dynamicznych struktur, choć często o tym niewielu pamięta i pomijana jest tak kwestia w większości zastosowań.

Komentarze (0)

Dodaj komentarz

/dozwolony markdown/

/nie zostanie opublikowany/