Wyjątki w konstruktorze

17 października 2008

Wyjątki są najprostszą i najlepszą możliwością zgłoszenia błędów zaistniałych w konstruktorze, przez których tworzenie obiektu zakończyło się porażką.  Bez sytuacji wyjątkowych nie ma bezpośredniego prostego mechanizmu  zgłoszenia zaistniałej sytuacji, ponieważ konstruktor nie przekazuje żadnego typu i wartości, która mogłaby przekazać informacje o zaistniałym błędzie.

Oczywiście można obejść ten problem poprzez zastosowanie dodatkowej zmiennej lokalnej do określania stanu obiektu i sprawdzać stan po utworzeniu obiektu lub wykorzystać nielokalną, umowną zmienną, która „powie” nam, że tworzenie zakończyło się niepowodzeniem.

Rzucanie wyjątków w konstruktorze niesie pewne problemy, poczynając od wycieków pamięci i zasobów, do niespójności obiektów.

Problem nie istnieje dla obiektów zawierających podobiekty i rzucanie wyjątków z tych podobiektów w konstruktorze. Mechanizm obsługi sytuacji wyjątkowych w języku C++ gwarantuje nam, że podobiekty całkowicie skonstruowane będą zniszczone, podobiekty jeszcze nie całkiem skonstruowane – nie.

Inna sprawa, jeśli posiadamy składowe wskaźnikowe i w ctor tworzymy jakieś obiekty a w dtor je usuwamy (to samo dotyczy dowolnych zasobów). Jeśli podobiekty te mogą rzucić wyjątkiem w czasie ich konstruowania, powinniśmy nasz konstruktor odpowiednio przed tym zabezpieczyć, aby nie dopuścić do wycieków zasobów lub naruszenia spójności obiektu.

Ale o tym nie ma co opowiadać, bo każdy o tym wie ;)

Chciałem tylko wspomnieć o ciekawej, acz rzadko spotykanej konstrukcji bloku try-catch dla list inicjalizacyjnych konstruktora, która rozciąga się na cale ciało i listę inicjalizacyjną:

class Bar {
public:
	Bar(Foo foo1, Foo foo2)
	try : m_foo1(foo1), m_foo1(foo2) {
		// cialo konstruktora
		}
	catch (...) {
		throw;
	}
 
	~Bar() {}
 
private:
	Foo m_foo1;
	Foo m_foo2;;
};

Konstruktory klasy Foo mogą rzucić wyjątkiem w czasie tworzenia, bądz kopiowania.

W kodzie obsługi wyjątku nie możemy odwoływać się do żadnych elementów obiektu Bar, ponieważ on nie istnieje. Aczkolwiek w tym przypadku nie jest to dla nas wielkim problemem, bo obiekty klasy Foo są tworzona na stosie, dlatego jak przedstawiono w punkcie o obiektach prostych, zabezpieczeniem zajmą się mechanizmy języka, gwarantujące spójność i zniszczenie utworzonych podobiektów.

Innym sposobem rozwiązania problemu rzucania wyjątków w konstruktorze przy składowych wskaźnikowych i tworzeniu podobiektów jest skorzystanie z idiomu RAII.

Wtedy składową staje się inny obiekt – zarządca zasobu – prosta „nakładka” na wskaźnik, a przez to problem zasobów możemy znów pozostawić mechanizmom języka, które zadbają o zniszczenie obiektu.

Najprościej wykorzystać tutaj auto_ptr z biblioteki standardowej, ale należy robić to z rozwagą, aby nie popaść w jeszcze większe tarapaty.

Ok., już nie marudzę ;)

Więcej głębszych informacji można znaleźć w GotW #66: Constructor Failures.

Podobne notatki:

Może zainteresują Cię również następujące, pododbne notatki:

Nikt jeszcze nie skomentował tego wpisu.
Możesz być pierwszy.

Dodaj swój komentarz

Możesz użyć tych tagów XHTML-a: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

Jeśli chcesz wstawić kilku linijkowy fragment kodu, użyj tagów <pre lang="x"></pre> (gdzie x język kodu np. cpp, perl, html). W ten sposób kod zostanie odpowiednio sformatowany i pokolorowany przez system.

Uwaga!

Na tym blogu działa system cache oraz filtr antyspamowy. Twój komentarz może być widoczny na stronie z pewnym opóźnieniem. Proszę o cierpliwość. Jeśli utraciłeś już wszystkie jej zasoby poinformuj mnie o tym, być może system uznał Cię za spamera ;)