You are on page 1of 20

IDZ DO

PRZYKADOWY ROZDZIA
SPIS TRECI

KATALOG KSIEK
KATALOG ONLINE
ZAMW DRUKOWANY KATALOG

TWJ KOSZYK
DODAJ DO KOSZYKA

CENNIK I INFORMACJE
ZAMW INFORMACJE
O NOWOCIACH
ZAMW CENNIK

CZYTELNIA
FRAGMENTY KSIEK ONLINE

Wydawnictwo Helion
ul. Chopina 6
44-100 Gliwice
tel. (32)230-98-63
e-mail: helion@helion.pl

Wyjtkowy styl jzyka C++.


40 nowych amigwek,zada
programistycznych i rozwiza
Autor: Herb Sutter
Tumaczenie: Tomasz Walczak
ISBN: 83-246-0061-2
Tytu oryginau: Exceptional C++ Style:
40 New Engineering Puzzles, Programming
and Solutions (C++ in Depth Series)
Format: B5, stron: 304
Zaprojektuj i napisz wydajniejsze oprogramowanie
Poznaj najlepsze metody stosowania biblioteki STL
Zaimplementuj wydajne mechanizmy zarzdzania pamici i zasobami
Zoptymalizuj kod rdowy swoich aplikacji
Projektowanie i tworzenie wydajnych aplikacji to sztuka znajdowania kompromisu
pomidzy kosztami a funkcjonalnoci, elegancj i atwoci pielgnacji oraz midzy
elastycznoci i nadmiern zoonoci. Znalezienie takiego zotego rodka jest
zadaniem wymagajcym znajomoci najlepszych praktyk programistycznych. Guru
jzyka C++, Herb Sutter, w ksice Wyjtkowy jzyk C++. 40 nowych amigwek,
zada programistycznych i rozwiza przedstawi najistotniejsze zasady stosowania
biblioteki standardowej, reguy inynierii oprogramowania i wiele innych tematw
zwizanych z tworzeniem programw w jzyku C++. Ksika ta jest kontynuacj jego
rozwaa i rad dla programistw chccych pisa wydajne oprogramowanie.
W ksice Herb Sutter koncentruje si na stylu pisania kodu rdowego. Przedstawia
40 nowych przykadw, dziki ktrym dowiesz si nie tylko, co si dzieje w programie,
ale take w jaki sposb. Czytajc j, poznasz nowe sposoby stosowania kluczowych
elementw jzyka C++. Kade z zagadnie przedstawione jest w formie zagadki
z rozwizaniem. Dziki temu lepiej zapamitujemy metodyk postpowania, co uatwia
wykorzystanie jej w codziennej pracy.
Zasady programowania uoglnionego
Niestandardowe zastosowania biblioteki STL
Bezpieczna obsuga wyjtkw
Reguy projektowania klas
Efektywne zarzdzanie pamici
Optymalizowanie aplikacji pod ktem wydajnoci
Unikanie puapek w kodzie
Jeli chcesz poprawi stabilno i wydajno swoich programw, signij po kolejny
poradnik autorstwa Herba Suttera.

Spis treci
Przedmowa ....................................................................................... 7
Rozdzia 1. Programowanie uoglnione i biblioteka standardowa jzyka C++ ...... 13
Zagadnienie 1. Poprawne i niepoprawne uywanie klasy vector .................................. 14
Zagadnienie 2. Folwark metod formatowania. Cz 1. sprintf .................................... 21
Zagadnienie 3. Folwark metod formatowania. Cz 2.
Standardowe (lub olniewajce) alternatywy
Zagadnienie 4. Funkcje skadowe biblioteki standardowej .......................................... 36
Zagadnienie 5. Smaczki programowania uoglnionego. Cz 1. Podstawy (sic!) ...... 40
Zagadnienie 6. Smaczki programowania uoglnionego. Cz 2.
Wystarczajco oglne? ........................................................................ 43
Zagadnienie 7. Dlaczego nie naley specjalizowa szablonw funkcji? ...................... 49
Zagadnienie 8. Zaprzyjanianie szablonw .................................................................. 55
Zagadnienie 9. Ograniczenia sowa kluczowego export. Cz 1. Podstawy ............... 64
Zagadnienie 10. Ograniczenia sowa kluczowego export. Cz 2.
Interakcje, uyteczno i wskazwki ................................................... 72

Rozdzia 2. Zagadnienia i techniki zwizane z bezpieczn obsug wyjtkw ...... 83


Zagadnienie 11. Bloki try i catch .................................................................................... 84
Zagadnienie 12. Bezpieczna obsuga wyjtkw czy warto? ...................................... 88
Zagadnienie 13. Specyfikacja wyjtkw z praktycznego punktu widzenia .................... 91

Rozdzia 3. Projektowanie klas, dziedziczenie i polimorfizm .............................. 101


Zagadnienie 14. Prosz zachowa porzdek! ............................................................... 102
Zagadnienie 15. Uywanie i naduywanie prawa dostpu ........................................... 105
Zagadnienie 16. (W wikszoci) prywatne ................................................................... 110
Zagadnienie 17. Hermetyzacja ..................................................................................... 118
Zagadnienie 18. Funkcje wirtualne .............................................................................. 127
Zagadnienie 19. Wymuszanie przestrzegania regu w klasach pochodnych ................. 135

Spis treci

Rozdzia 4. Zarzdzanie pamici i zasobami ................................................... 147


Zagadnienie 20. Kontenery w pamici. Cz 1. Poziomy zarzdzania pamici .............147
Zagadnienie 21. Kontenery w pamici. Cz 2. Ile miejsca zajmuj naprawd? .............150
Zagadnienie 22. O new, a przy okazji o throw. Cz 1. Oblicza new ..............................157
Zagadnienie 23. O new, a przy okazji o throw. Cz 2.
Praktyczne zagadnienia dotyczce zarzdzania pamici .......................164

Rozdzia 5. Optymalizacja i wydajno ............................................................ 173


Zagadnienie 24. Optymalizacja za pomoc const? .............................................................173
Zagadnienie 25. Powrt inline .............................................................................................178
Zagadnienie 26. Format danych i wydajno. Cz 1.
Kiedy w gr wchodzi kompresja .............................................................186
Zagadnienie 27. Format danych a wydajno. Cz 2. Zabawa z bitami .........................190

Rozdzia 6. Puapki, zasadzki i amigwki ....................................................... 199


Zagadnienie 28. Sowa kluczowe, ktrych nie ma (lub, inaczej mwic, komentarze) ....199
Zagadnienie 29. Czy to inicjalizacja? .................................................................................206
Zagadnienie 30. Podwjna lub adna ..................................................................................210
Zagadnienie 31. Kod w amoku ...........................................................................................213
Zagadnienie 32. Literwki? Jzyk graficzny i inne ciekawostki ........................................218
Zagadnienie 33. Operatory, wszdzie operatory .................................................................220

Rozdzia 7. Studia przypadku .......................................................................... 227


Zagadnienie 34. Tablice indeksujce .......................................................................................227
Zagadnienie 35. Uoglnione wywoania zwrotne ...................................................................238
Zagadnienie 36. Unie konstrukcyjne .......................................................................................246
Zagadnienie 37. Rozciganie monolitw. Cz 1. Spojrzenie na std::string ........................263
Zagadnienie 38. Rozciganie monolitw. Cz 2. Rozkad klasy std::string na czynniki ...267
Zagadnienie 39. Rozciganie monolitw. Cz 3. Odchudzanie klasy std::string ...............276
Zagadnienie 40. Rozciganie monolitw. Cz 4. Powrt klasy std::string .........................279

Bibliografia .......................................................................................... 289


Skorowidz ............................................................................................ 293

Rozdzia 2.

Zagadnienia i techniki
zwizane z bezpieczn
obsug wyjtkw
Obsuga wyjtkw jest podstawowym mechanizmem zgaszania bdw w jzyku C++
i innych wspczesnych jzykach programowania. W ksikach Exceptional C++1
[Sutter00] oraz More Exceptional C++2 [Sutter02] szczegowo przedstawiam wiele
zagadnie zwizanych z okreleniem, czym jest bezpieczna obsuga wyjtkw i jak
pisa kod z bezpieczn obsug wyjtkw. Opisuj take waciwoci jzyka i interakcje,
o ktrych musisz pamita.
W tym rozdziale kontynuuj te rozwaania, skupiajc si na pewnych waciwociach
jzyka specyficznych dla obsugi wyjtkw. Na pocztku odpowiadam na odwieczne
pytanie czy bezpieczna obsuga wyjtkw to tylko wpisywanie try i catch w odpowiednich miejscach? Jeli nie, to co jeszcze? Nad czym musisz si zastanowi, tworzc
w programie schemat obsugi wyjtkw?
Odchodzc nieco od tematu warto powici cae zagadnienie, aby przedstawi
powody, dla ktrych pisanie kodu z bezpieczn obsug wyjtkw to czysta korzy.
Takie postpowanie wie si ze stylem programowania, ktry prowadzi do bardziej
stabilnego i atwiejszego w pielgnacji kodu, pomijajc nawet korzyci wynikajce ze
stosowania wyjtkw. Jest jednak pewne ograniczenie tych korzyci i mylenia na zasadzie im wicej, tym lepiej. W przypadku specyfikacji wyjtkw ograniczenie to jest
szczeglnie widoczne. Dlaczego wyjtki istniej w jzyku? Dlaczego ich wystpowanie
jest dobrze uzasadnione? I dlaczego mimo to nie powiniene uywa ich w programach?

Wydanie polskie: Wyjtkowy jzyk C++. 47 amigwek, zada programistycznych i rozwiza,


WNT, 2002 przyp. tum.

Wydanie polskie: Wyjtkowy jzyk C++. 40 nowych amigwek, zada programistycznych i rozwiza,
Helion, 2005 przyp. tum.

Rozdzia 2. Zagadnienia i techniki zwizane z bezpieczn obsug wyjtkw

84

Tego i innych rzeczy dowiesz si, czerpic z fontanny wiedzy wspczesnego wyjtkowego rodowiska programistw.
Zagadnienie 11. Bloki try i catch

Stopie trudnoci: 3

Czy bezpieczna obsuga wyjtkw to tylko wpisywanie try i catch w odpowiednich miejscach?
Jeli nie, to co jeszcze? Nad czym musisz si zastanowi, tworzc w programie schemat
obsugi wyjtkw?

Pytanie profesora
1. Do czego suy blok try?

Pytania magistra
2. Pisanie kodu z bezpieczn obsug wyjtkw polega gwnie na wpisywaniu try
i catch w odpowiednich miejscach. Przeprowad analiz tego stwierdzenia.
3. Kiedy naley stosowa bloki try i catch? Kiedy nie naley ich stosowa?

Przedstaw odpowied w formie wskazwki co do dobrego stylu programowania.

Rozwizanie
Zabawa w berka
1. Do czego suy blok try?

Blok try to fragment kodu (zoone wyraenie), ktry program prbuje wykona. Po
bloku tym znajduje si jeden lub wicej blokw catch, do ktrych program przechodzi
w sytuacji przechwycenia zgoszonego w bloku try wyjtku odpowiedniego typu. Na
przykad:
// Przykad 11.1. Przykadowy blok try
//
try {
if ( pewien_warunek )
throw string( "To jest cig znakw" );
else if ( pewien_inny_warunek )
throw 42;
}
catch ( const string& ) {
// Zrb co, jeli przechwycony zosta cig znakw
}
catch(...) {
// Zrb co, jeli przechwycony zostanie dowolny inny wyjtek
}

W przykadzie 11.1 kod w bloku try moe zgosi jako wyjtek cig znakw lub liczb
cakowit, a moe te w ogle nie zgosi wyjtku.

Zagadnienie 11. Bloki try i catch

85

ycie to nie tylko zabawa w berka


2. Pisanie kodu z bezpieczn obsug wyjtkw polega gwnie na wpisywaniu
w odpowiednich miejscach try i catch. Przeprowad analiz tego stwierdzenia.

Krtko mwic, takie stwierdzenie obrazuje podstawowy bd w rozumieniu bezpieczestwa wyjtkw. Wyjtki s po prostu jednym ze sposobw zgaszania bdw i na
pewno wiesz, e pisanie kodu odpornego na bdy nie polega jedynie na sprawdzaniu
zwracanych wartoci i obsudze warunkw powodujcych te bdy.
W rzeczywistoci okazuje si, e bezpieczna obsuga wyjtkw rzadko wie si z wpisywaniem try i catch im rzadziej, tym lepiej. Powiniene te zawsze pamita, e
o bezpieczn obsug wyjtkw trzeba zadba ju na etapie projektowania kodu. Nie
jest to element, ktry mona doda na kocu, dopisujc kilka dodatkowych instrukcji
catch.
Z pisaniem kodu z bezpieczn obsug wyjtkw wi si trzy gwne zagadnienia:
1. Gdzie i kiedy naley zgasza wyjtki? Ta kwestia dotyczy umieszczania
instrukcji throw w odpowiednich miejscach. W szczeglnoci musisz rozway:

Ktre fragmenty kodu powinny zgasza wyjtki? Wie si to z wyborem


bdw, ktre obsugiwane bd za pomoc zgoszenia wyjtku, a nie
za pomoc zwracania wartoci informujcej o bdzie lub innej techniki.

Ktre fragmenty kodu nie powinny zgasza wyjtkw? W szczeglnoci


ktre fragmenty kodu musz by bezbdne? (Patrz te zagadnienie 12.
oraz [Sutter99]).

2. Gdzie i kiedy naley obsuy wyjtek? Jest to jedyne zagadnienie zwizane


z wpisywaniem w odpowiednich miejscach try i catch, co jednak zwykle mona

zautomatyzowa. Po pierwsze, zastanw si nad poniszymi pytaniami:

Ktre fragmenty kodu mog przechwytywa bdy? Wymaga to okrelenia,


ktre fragmenty kodu maj odpowiedni kontekst i informacje pozwalajce
na obsug bdu zgoszonego przez wyjtek (zwykle poprzez przeksztacenie
wyjtku na inn posta). Zauwa, e take przechwytujcy kod musi mie
informacje niezbdne do porzdkowania, na przykad do zwolnienia
dynamicznie przydzielonych zasobw.

Ktre fragmenty kodu powinny przechwytywa bdy? W tym miejscu


naley wybra najbardziej do tego odpowiednie spord fragmentw kodu,
ktre mog przechwytywa bdy.

Po udzieleniu odpowiedzi na te pytania zwr uwag na to, e stosowanie idiomu alokacja zasobw jest inicjalizacj pozwala wyeliminowa wiele blokw try dziki automatyzacji porzdkowania. Jeli opakujesz dynamicznie przydzielane zasoby w zarzdzajce nimi obiekty, zwykle destruktor bdzie mg zwolni je automatycznie bez
potrzeby uywania blokw try i catch. Taka sytuacja jest oczywicie podana, nie
wspominajc, e taki kod jest zwykle atwiejszy do napisania i zrozumienia.

Rozdzia 2. Zagadnienia i techniki zwizane z bezpieczn obsug wyjtkw

86

Powiniene automatycznie obsugiwa wyjtki zwizane z porzdkowaniem za


pomoc destruktorw, a nie za pomoc blokw try i catch.
3. Jeli wyjtek zostanie zgoszony w dowolnym miejscu, to czy pozostaa cz

kodu bdzie bezpieczna? To zagadnienie dotyczy poprawnego zarzdzania


zasobami, zwizanego z unikaniem wyciekw, z pielgnacj klas,
z niezmiennikami i innymi elementami poprawnoci programu. Mwic
inaczej, polega to na zapobieganiu bdom programu wynikajcym
z przechodzenia wyjtku z miejsca jego zgoszenia przez te fragmenty kodu,
ktre nie powinny troszczy si o wyjtek przed jego dotarciem do miejsca
przechwycenia i obsugi. Dla wikszoci programistw, z ktrymi pracowaem,
jest to zdecydowanie najbardziej czasochonny i najtrudniejszy do opanowania
aspekt bezpiecznej obsugi wyjtkw.
Zauwa, e tylko jeden z tych trzech problemw ma co wsplnego z pisaniem try oraz
catch, a nawet ten mona atwo rozwiza dziki rozsdnemu zastosowaniu destruktorw do zautomatyzowania porzdkowania.
3. Kiedy naley stosowa bloki try i catch? Kiedy nie naley ich stosowa? Przedstaw

odpowied w formie wskazwki co do dobrego stylu programowania.


Poniej przedstawiam pewne sugestie. W skrcie:
1. Okrel oglny schemat zgaszania i obsugi bdw dla swoich aplikacji lub

podsystemw, a nastpnie stosuj go konsekwentnie. W szczeglnoci schemat


ten powinien zawiera podstawowe elementy przedstawione poniej (zwykle
zawiera ich znacznie wicej):

Zgaszanie bdw. Okrel, jakie rodzaje bdw funkcje powinny zgasza


oraz w jaki sposb. Staraj si uywa wyjtkw zamiast innych metod
zgaszania bdw. Zwykle dobrze jest dla kadej sytuacji okreli jedn
domyln metod, ktra jest najbardziej czytelna i atwa w pielgnacji. Na
przykad wyjtki s najbardziej uyteczne dla konstruktorw i operatorw,
ktre nie mog zwraca wartoci, a take w sytuacjach, kiedy miejsce
zgaszania bdu jest znacznie oddalone od miejsca jego obsugi.

Przekazywanie bdw. Midzy innymi powiniene zdefiniowa granice,


ktrych wyjtki nie powinny przekracza. Zwykle s to granice moduw
lub API.

Obsuga bdw. Midzy innymi tam, gdzie to moliwe, powiniene przenie


funkcje zarzdzajce porzdkowaniem do obiektw i ich destruktorw,
zamiast do blokw try i catch.

2. Umieszczaj instrukcje throw w tych miejscach wykrycia bdw, w ktrych

bdw nie mona obsuy na miejscu. Kod, w ktrym mona natychmiast


rozwiza problem, oczywicie nie musi tego problemu zgasza.
W przypadku kadej operacji opisz, jakie wyjtki moe zgasza i dlaczego.
Powinno by to czci dokumentacji kadej funkcji i moduu. Nie musisz
pisa specyfikacji wyjtkw dla kadej funkcji (a nawet nie powiniene patrz

Zagadnienie 11. Bloki try i catch

zagadnienie 13.), ale powiniene jasno i dokadnie opisa, czego uytkownik


moe si spodziewa. Obsuga bdw jest czci interfejsu funkcji i moduw.
3. Umieszczaj try i catch w miejscach, w ktrych program ma wystarczajce

informacje do obsugi bdu, jego translacji oraz do wymuszenia ogranicze


okrelanych przez schemat obsugi bdw. Uwaam, e istniej trzy gwne
powody dodawania blokw try i catch:

Obsuga bdu. To prosty przypadek. Zdarzy si bd, wiemy, co z nim


zrobi, wic robimy to. ycie toczy si dalej (oprcz samego wyjtku,
ktry odchodzi na zasuony odpoczynek). Pamitaj jeli to moliwe,
uyj destruktora; jeli nie, moesz uy blokw try i catch.

Translacja wyjtku. Oznacza to przechwycenie wyjtku, ktry zgasza


problem niszego poziomu, a nastpnie zgoszenie nowego wyjtku,
sformuowanego na wyszym poziomie w kontekcie przeksztacajcego
go kodu. Oryginalny wyjtek moe te zosta przeksztacony na inn
reprezentacj, na przykad na kod bdu.
Wyobra sobie przykadow klas sesji suc do obsugi komunikacji,
ktra dziaa dla hostw rnych typw oraz rnych protokow przesyania
danych. Prba otwarcia sesji na innym serwerze moe si nie powie z wielu
przyczyn niskiego poziomu, ktre moe wykry klasa obsugujca t sesj.
(Na przykad brak dostpu do sieci lub nieudzielenie dostpu ze strony
hosta zdalnego). Funkcja Open moe obsugiwa takie zdarzenia samodzielnie,
dlatego bdw nie trzeba zgasza funkcji wywoujcej, w kontekcie ktrej
nie istniej informacje o pakiecie Foo lub o tym, co zrobi, jeli zwrcona
zostanie nieznana warto. Klasa sesji bezporednio obsuguje wewntrzne
bdy niskiego poziomu, utrzymuje poprawny stan oraz zgasza bdy
wyszego poziomu lub wyjtki, aby poinformowa funkcj wywoujc,
e otwarcie sesji zakoczyo si niepowodzeniem.
void Session::Open( /*...*/ ) {
try {
// Caa operacja
}
catch ( const ip_error& err ) {
// - Zrb co z bdem IP
// - porzdkowanie
throw Session::OpenFailed();
}
catch ( const KerberosAuthentFail& err ) {
// - Zrb co z bdem autoryzacji
// - porzdkowanie
throw Session::OpenFailed();
}
// .. itd.
}

Przechwytywanie za pomoc catch(...) wyjtkw na granicach


podsystemw lub innych jednostek czasu wykonania. Operacja ta zwykle
wie si z translacj bdu, zwykle na kod bdu lub inn reprezentacj
rn od wyjtkw. Na przykad, kiedy stos rozwija si do C API, masz
tylko dwie moliwoci zwrci kod bdu do aktualnej funkcji API lub

87

88

Rozdzia 2. Zagadnienia i techniki zwizane z bezpieczn obsug wyjtkw

ustawi stan bdu, co pozwala funkcji wywoujcej sprawdzi go za pomoc


odpowiedniej funkcji API GetLastError.
Okrel oglny schemat zgaszania bdw oraz ich obsugi dla aplikacji lub podsystemu,
a nastpnie stosuj go konsekwentnie. Pamitaj o schemacie zgaszania bdw,
przekazywania bdw oraz ich obsugi.
Umieszczaj instrukcje throw w tych miejscach wykrycia bdw, w ktrych bdw
nie mona obsuy na miejscu.
Umieszczaj bloki try i catch w miejscach, w ktrych program ma wystarczajce
informacje do obsugi bdu i jego translacji oraz do wymuszenia ogranicze
okrelanych przez schemat obsugi bdw (na przykad przechwytuje za pomoc
catch(...) wszystkie bdy na granicach podsystemw lub innych jednostek czasu
wykonania).

Podsumowanie
Pewien mdrzec powiedzia kiedy:
prowad, podaj ladem albo usu si z drogi!
W przypadku analizy bezpiecznej obsugi wyjtkw mona to sparafrazowa tak:
zgaszaj, przechwytuj albo usu si z drogi!
W praktyce ostatni przypadek usu si z drogi stanowi istotn cz analizy
i testw bezpieczestwa wyjtkw. Jest to podstawowy powd, dla ktrego pisanie kodu
z bezpieczn obsug wyjtkw nie polega gwnie na odpowiednim wpisywaniu try
i catch. Jego istot jest schodzenie z toru pocisku w odpowiednim momencie.
Zagadnienie 12. Bezpieczna obsuga wyjtkw czy warto? Stopie trudnoci: 7
Czy pisanie kodu z bezpieczn obsug wyjtkw jest warte wysiku? Kwestia ta nie powinna
budzi adnych wtpliwoci jednak czasem nadal si tak dzieje.

Pytania do profesora
1. Powtrka krtko zdefiniuj, jakie gwarancje, zdaniem Abrahamsa, powinna

zapewnia bezpieczna obsuga wyjtkw (sab, mocn i niezawodnoci).


2. Kiedy warto pisa kod, ktry zapewnia:
a) gwarancj sab?
b) gwarancj mocn?
c) gwarancj niezawodnoci?

Zagadnienie 12. Bezpieczna obsuga wyjtkw czy warto?

89

Rozwizanie
Gwarancje Abrahamsa
1. Powtrka krtko zdefiniuj, jakie gwarancje, zdaniem Abrahamsa, powinna

zapewnia bezpieczna obsuga wyjtkw (sab, mocn i niezawodnoci).


Gwarancja saba (ang. basic guarantee) mwi, e nieudana operacja moe zmienia
stan programu, ale nie moe powodowa wyciekania zasobw, a zmienione obiekty
i moduy wci mog zosta usunite albo uyte w stabilny (cho niekoniecznie przewidywalny) sposb.
Gwarancja mocna (ang. strong guarantee) wie si z semantyk transakcji typu
commit-rollback. Nieudana operacja nie moe powodowa zmiany stanu programu
w zakresie zmiany obiektw, ktrych dotyczy. Oznacza to brak efektw ubocznych, ktre
wpywayby na obiekty, wczajc w to poprawno ich stanu, ich zawarto oraz stan
obiektw pomocniczych, takich jak wskaniki na zawarto kontenerw.
Wreszcie gwarancja niezawodnoci (ang. nofail guarantee) mwi, e niepowodzenie
nie moe si zdarzy. W kategoriach wyjtkw oznacza to, e operacja nie zgasza
wyjtkw. (Abahams i inni, wczajc w to wczeniejsze ksiki z serii Exceptional
C++, pocztkowo uywali nazwy gwarancja niezgaszania. Zmieniem nazw na
gwarancja niezawodnoci, poniewa gwarancja ta dotyczy kadej formy obsugi bdw, zarwno za pomoc wyjtkw, jak i za pomoc innych mechanizmw, na przykad
kodu bdu.

Kiedy warto stosowa mocniejsze gwarancje?


2. Kiedy warto pisa kod, ktry zapewnia:
a) gwarancj sab?
b) gwarancj mocn?
c) gwarancj niezawodnoci?

Zawsze warto pisa kod, ktry zapewnia cho jedn z tych gwarancji. Wynika to z kilku
przyczyn:
1. Parafrazujc znane powiedzenie wyjtki si zdarzaj. Po prostu tak si

dzieje. Biblioteka standardowe je zgasza. Jzyk je zgasza. Programici


musz uwzgldnia to w kodzie. Na szczcie nie jest to zbyt skomplikowane,
poniewa wiemy, jak trzeba to robi. Wymaga to wyksztacenia kilku nawykw
i sumiennego ich przestrzegania ale w kocu tego samego wymaga
nauczenie si programowania z uyciem kodw bdw.
Duym problemem jest, jak zawsze, obsuga bdw jako taka. Techniczna
realizacja sposobu zgaszania bdw za pomoc zwracania kodu bdu lub
zgaszania wyjtku prawie cakowicie naley do skadni, podczas gdy gwne
rnice le w semantyce zgaszania, dlatego kada z tych technik wymaga
specyficznego podejcia.

90

Rozdzia 2. Zagadnienia i techniki zwizane z bezpieczn obsug wyjtkw


2. Pisanie kodu z bezpieczn obsug wyjtkw jest korzystne. Kod z bezpieczn

obsug wyjtkw i dobry kod id w parze. Te same techniki, ktre pomagaj


tworzy kod z bezpieczn obsug wyjtkw, wyznaczaj standardy, ktrych
i tak powinnimy przestrzega. Oznacza to, e techniki tworzenia kodu
z bezpieczn obsug wyjtkw s korzystne same w sobie, nawet pomijajc
kwesti samych wyjtkw.
Aby si o tym przekona, przyjrzyj si opisanym przeze mnie i przez innych autorw
podstawowym technikom, ktre uatwiaj bezpieczn obsug wyjtkw:
Stosuj zasad alokacja zasobw jest inicjalizacj (ang. resource acquisition

is initialization RAII) do zarzdzania wasnoci zasobw. Uywanie obiektw


majcych zasoby, jak klasy Lock lub wskaniki shared_ptr (patrz [Boost,
Sutter02a]) jest zwykle dobrym pomysem. Nie powinno Ci zaskoczy,
e wrd wielu ich zalet moesz take znale bezpieczn obsug wyjtkw.
Ile razy widziae ju funkcje (mwimy tu oczywicie o funkcjach napisanych
przez innych programistw, nie o Twoim kodzie), w ktrych jedna ze cieek
prowadzcych do szybkiego zwrcenia wyniku nie wykonuje odpowiedniego
porzdkowania, poniewa nie jest ono automatycznie zarzdzane za pomoc RAII?
Stosuj zasad: wykonaj wszystkie dziaania, a nastpnie zatwierd je, uywajc

jedynie operacji, ktre nie zgaszaj bdw, aby unikn zmiany wewntrznego
stanu, dopki nie upewnisz si, e wszystkie operacje zakocz si powodzeniem.
Takie programowanie transakcyjne jest bardziej przejrzyste i bardziej bezpieczne
nawet wtedy, kiedy uywasz kodw bdw. Ile razy widziae ju funkcje
(oczywicie ponownie chodzi tu o cudze funkcje, nie Twoje), w ktrych jedna
ze cieek prowadzcych do szybkiego zwrcenia wyniku powoduje zmian
stanu obiektu, poniewa zmiana wystpia przed pniejsz nieudan operacj?
Stosuj zasad jedna klasa (lub funkcja), jedno zadanie. Funkcje, ktre
wykonuj wiele zada jednoczenie, takie jak Stack::Pop lub
EvaluateSalaryAndReturnName opisane w zagadnieniach 10. i 18. w ksice

Exceptional C++3 [Sutter00], rzadko oferuj w peni bezpieczn obsug


wyjtkw. Wiele problemw zwizanych z bezpieczn obsug wyjtkw
mona uproci lub wyeliminowa bez dugiego zastanawiania si, stosujc
si do zasady jedna funkcja, jedno zadanie. Wskazwka ta jest duo starsza
ni wiedza o moliwoci zastosowania jej do bezpiecznej obsugi wyjtkw.
Pomys ten jest praktyczny sam w sobie.

Stosowanie si do tych wskazwek przysporzy Ci samych korzyci.


Biorc pod uwag powysze rozwaania w jakich sytuacjach naley uywa poszczeglnych gwarancji? Poniej znajduje si krtka wskazwka, do ktrej stosuje si
biblioteka standardowa jzyka C++ i ktr take Ty moesz zastosowa z korzyci
dla jakoci pisanego kodu.

Wydanie polskie: Wyjtkowy jzyk C++. 47 amigwek, zada programistycznych i rozwiza,


WNT, 2002 przyp. tum.

Zagadnienie 13. Specyfikacja wyjtkw z praktycznego punktu widzenia

91

Funkcja powinna zawsze by zgodna z najbardziej restrykcyjnymi gwarancjami,


ktrych stosowanie nie pociga za sob kosztw po stronie funkcji wywoujcej,
niewymagajcej takich gwarancji.
Jeli wic funkcja moe zapewnia gwarancj niezawodnoci bez generowania kosztw po stronie funkcji wywoujcej, ktra nie potrzebuje takiej gwarancji, powiniene
zastosowa wanie j. Pamitaj take, e niektre kluczowe funkcje musz by operacjami niezawodnymi.
Nigdy nie umoliwiaj zgaszania wyjtku przez destruktory, funkcje zwalniajce
zasoby oraz funkcje zamieniajce obiekty. W przeciwnym razie czsto niemoliwe
jest wykonanie porzdkowania w sposb niezawodny i bezpieczny.
Jeli funkcja moe zapewnia tylko gwarancj mocn bez generowania kosztw po
stronie niektrych uytkownikw, powiniene j zastosowa. Zauwa, e funkcja
vector::insert jest przykadem funkcji, ktra nie zapewnia gwarancji mocnej, poniewa wymuszaoby to tworzenie kompletnej kopii zawartoci wektora przy wstawianiu
kadego elementu, a nie wszyscy programici dbaj o gwarancj mocn do tego stopnia, e byliby gotowi pogodzi si z tak duym narzutem. Ci, ktrym na tym zaley,
mog samodzielnie opakowa funkcj vector::insert w gwarancj mocn. Jest to
bardzo proste wystarczy skopiowa wektor, wstawi nowy element do kopii, a kiedy
operacja ta si powiedzie, zamieni oryginalny wektor na kopi i gotowe.
W przeciwnym razie funkcja powinna zapewnia gwarancj sab.
Wicej informacji o tych zagadnieniach, midzy innymi o niezgaszajcej wyjtkw
wersji funkcji swap lub o tym, dlaczego destruktory nie zgaszaj wyjtkw, znajdziesz
w ksikach Exceptional C++4 [Sutter00] oraz More Exceptional C++5 [Sutter02].
Zagadnienie 13. Specyfikacja wyjtkw
z praktycznego punktu widzenia

Stopie trudnoci: 6

Teraz, kiedy spoeczno programistw posiada ju pewne dowiadczenia ze specyfikacj


wyjtkw, moemy podsumowa, gdzie i jak naley j dodawa, aby osign najlepsze
efekty. To zagadnienie dotyczy uytecznoci specyfikacji wyjtkw (lub jej braku), a take
stopnia jej zalenoci od kompilatora.

Pytania do magistra
1. Co si dzieje, kiedy zgaszany jest wyjtek niezgodny ze specyfikacj? Dlaczego?

Opisz podstawowe przyczyny istnienia tej cechy w jzyku C++.


2. Jakie wyjtki moe zgasza kada z poniszych funkcji?
4

Wydanie polskie: Wyjtkowy jzyk C++. 47 amigwek, zada programistycznych i rozwiza,


WNT, 2002 przyp. tum.

Wydanie polskie: Wyjtkowy jzyk C++. 40 nowych amigwek, zada programistycznych i rozwiza,
Helion, 2005 przyp. tum.

Rozdzia 2. Zagadnienia i techniki zwizane z bezpieczn obsug wyjtkw

92

int Func();
int Gunc() throw();
int Hunc() throw(A, B);

Pytania do profesora
3. Czy specyfikacja wyjtkw stanowi o typie funkcji? Wyjanij.
4. Czym s specyfikacje wyjtkw i co robi? Wyjanij szczegowo.
5. Kiedy warto doda do funkcji specyfikacj wyjtkw? Co powoduje, e decydujesz

si doda t waciwo, a co nie?

Rozwizanie
Prace nad nowym standardem jzyka C++, C++0x s dobr okazj do analizy tego,
czego nauczylimy si na podstawie dowiadcze z obecnym standardem [C++03].
Znaczca wikszo standardowych waciwoci jzyka C++ jest przydatna i to wanie
o nich mwi si najwicej, poniewa nie ma sensu rozwodzi si nad mniej istotnymi
cechami. Te sabsze i mniej uyteczne waciwoci s przewanie ignorowane i zanikaj z braku uytkownikw, a wiele osb zapomni nawet o ich istnieniu (nie zawsze jest
to ze). Dlatego moesz znale stosunkowo mao artykuw na temat mniej przydatnych waciwoci, takich jak valarray, bitset, ustawienia lokalne czy dozwolone wyraenie 5[a] (chocia to ostatnie pojawia si w pewnej odmianie w jednym z kolejnych
zagadnie). To samo dotyczy, o czym si przekonasz, specyfikacji wyjtkw.
Przyjrzyjmy si teraz bliej dotychczasowym dowiadczeniom ze specyfikacj wyjtkw w jzyku C++.

Naruszanie specyfikacji
1. Co si dzieje, kiedy zgaszany jest wyjtek niezgodny ze specyfikacj? Dlaczego?

Opisz podstawowe przyczyny istnienia tej cechy w jzyku C++.


Celem specyfikacji wyjtkw jest umoliwienie w czasie wykonywania programu przeprowadzenia sprawdzianu, ktry pozwala zagwarantowa, e funkcja zgasza jedynie
wyjtki okrelonego typu lub nie zgasza adnych wyjtkw. Na przykad specyfikacja
wyjtkw funkcji przedstawionej poniej gwarantuje, e funkcja f zgasza jedynie wyjtki
typu A i B:
int f() throw( A, B );

Jeli zostanie zgoszony wyjtek spoza tej listy, zostanie wywoana funkcja unexpected.
Poniej przedstawiony jest prosty przykad:
// Przykad 13.1
//
int f() throw( A, B ) {
throw C();
}

// A i B nie s zwizane z C
// Powoduje wywoanie funkcji unexpected

Zagadnienie 13. Specyfikacja wyjtkw z praktycznego punktu widzenia

93

Za pomoc standardowej funkcji set_unexpected moesz zarejestrowa wasn funkcj


do obsugi wyjtkw nieoczekiwanych. Taka funkcja nie moe przyjmowa adnych
argumentw i musi zwraca typ void:
void MyUnexpectedHandler() { /*...*/ }
std::set_unexpected( &MyUnexpectedHandler; );

Pozostaje jednak pytanie, co powinna robi funkcja do obsugi nieoczekiwanego wyjtku? Na pewno nie moe zwraca sterowania za pomoc zwykej instrukcji return.
Moliwe s za to dwie opcje:
Funkcja ta moe przeksztaci wyjtek na inny, dopuszczony przez specyfikacj

wyjtkw, zgaszajc wasny wyjtek, zgodny ze specyfikacj. Rozwijanie


stosu jest nastpnie kontynuowane od miejsca, w ktrym zostao zatrzymane.
Funkcja moe te wywoa funkcj terminate, ktra koczy dziaanie programu.
Mona take zastpi sam funkcj terminate, jednak tylko inn funkcj, ktra

take koczy dziaanie programu.

Dotychczasowe dowiadczenia
atwo zrozumie przyczyny istnienia specyfikacji wyjtkw. W programie w jzyku
C++, jeli nie jest powiedziane inaczej, dowolna funkcja moe zgosi wyjtek dowolnego typu. Przyjrzyj si funkcji, ktr nazwaem Func (poniewa nazwa f jest straszliwie naduywana).
2. Jakie wyjtki moe zgasza kada z poniszych funkcji?
// Przykad 13.2(a)
//
int Func();
// Moe zgosi dowolny wyjtek

Domylnie funkcja Func moe zgosi dowolny wyjtek, jak jest to napisane w komentarzu. Czsto wiadomo, jakie wyjtki moe zgasza dana funkcja. Wtedy oczywicie
chcemy przekaza kompilatorowi i innym programistom informacje, ktre pozwol
ograniczy typy wyjtkw wydostajce si z funkcji. Na przykad:
// Przykad 13.2(b)
//
int Gunc() throw();
// Nie zgasza adnych wyjtkw
int Hunc() throw( A, B ); // Moe zgasza jedynie wyjtki typu A i B

W tych przypadkach specyfikacja wyjtkw funkcji pozwala okreli, jakie typy wyjtkw mog zgasza funkcje Gunc i Hunc. Komentarze w prosty sposb opisuj, co wynika z powyszych specyfikacji. Niedugo wrcimy do tego prosto, poniewa okazuje
si, e blisko rzeczywistoci jest zwodnicza.
Mona si intuicyjnie spodziewa, e opisanie wyjtkw, ktre moe zgasza dana
funkcja, jest korzystne, e im wicej informacji, tym lepiej. Jednak nie zawsze jest to
prawd, a diabe tkwi w szczegach. Chocia same zaoenia s poprawne, sposb
specyfikacji tej waciwoci w jzyku C++ powoduje, e specyfikacja wyjtkw nie
zawsze jest uyteczna i czsto okazuje si szkodliwa.

94

Rozdzia 2. Zagadnienia i techniki zwizane z bezpieczn obsug wyjtkw

Problem pierwszy system rozmywania typw


3. Czy specyfikacja wyjtkw jest czci typu funkcji? Wyjanij.

John Spicer, chluba Edison Design Group oraz autor duych fragmentw rozdziau dotyczcego szablonw w standardzie jzyka C++, nazwa podobno specyfikacj wyjtkw jzyka C++ systemem rozmywania typw. Jedn z najistotniejszych cech jzyka
C++ jest cisa kontrola typw i jest to bardzo korzystne. Dlaczego mielibymy nazywa specyfikacj wyjtkw systemem rozmywania typw, zamiast czci systemu
kontroli typw?
Istniej dwie proste przyczyny:
specyfikacja wyjtkw nie okrela typu funkcji,
oprcz sytuacji, w ktrych okrela.

Zastanw si najpierw nad przykadem, w ktrym specyfikacja wyjtkw nie okrela


typu funkcji. Przyjrzyj si krytycznie poniszemu fragmentowi kodu:
// Przykad 13.3(a). Nie moesz uy specyfikacji wyjtkw w definicji typu
//
void f() throw(A, B);
typedef void (*PF)() throw(A, B); // Bd skadni
PF pf = f;
// Z powodu bdu program nie dojdzie do tego miejsca

Specyfikacja wyjtkw w definicji typu jest niedozwolona. Jzyk C++ nie pozwala na
kompilacj powyszego kodu, dlatego specyfikacja wyjtkw nie moe okrela typu
funkcji przynajmniej nie w kontekcie definicji typu. Jednak w innych przypadkach
specyfikacja wyjtkw okrela typ funkcji, na przykad wtedy, kiedy napiszesz t sam
deklaracj funkcji bez sowa kluczowego typedef:
// Przykad 13.3(b). Moesz jednak, jeli pominiesz sowo kluczowe typedef
//
void f() throw(A, B);
void (*pf)() throw(A, B);
// Poprawne
pf = f;
// Poprawne

Moesz tak przypisa wskanik do funkcji, o ile specyfikacja wyjtkw obiektu docelowego nie jest bardziej restrykcyjna ni specyfikacja obiektu rdowego:
// Przykad 13.3(c). Take koszerne, z nisk zawartoci cukru i bez tuszczu.
//
void f() throw(A,B);
void (*pf)() throw(A,B,C);
// Poprawne
pf = f;
// Poprawne, typ pf jest mniej restrykcyjny

Specyfikacja wyjtkw okrela take typy funkcji wirtualnych, kiedy prbujesz je


przesoni:
// Przykad 13.3(d). Specyfikacja wyjtkw wpywa na funkcje wirtualne
//
class C {
virtual void f() throw(A,B);
// Taka sama specyfikacja wyjtkw
};

Zagadnienie 13. Specyfikacja wyjtkw z praktycznego punktu widzenia


class D : C {
void f();
};

95

// Bd, w tym miejscu specyfikacja ma znaczenie

Tak wic pierwszy problem zwizany ze specyfikacj wyjtkw we wspczesnym


jzyku C++ polega na tym, e stanowi one system rozmywania typw, ktry dziaa
wedug innych regu ni pozostaa cz systemu kontroli typw.

Problem drugi (nie)zrozumienie


Drugi problem wie si z wiedz o dziaaniu specyfikacji wyjtkw. Jak zauwayo
to wiele powaanych osb, wczajc w to autorw specyfikacji wyjtkw biblioteki
Boost [BoostES], programici zwykle uywaj specyfikacji wyjtkw w taki sposb,
jakby dziaaa ona wedug regu, ktre pasuj programistom, a nie tak, jak rzeczywicie dziaa.
Wynika z tego ponisze pytanie:
4. Czym s specyfikacje wyjtkw i co robi? Wyjanij szczegowo.

Wiele osb uwaa, e specyfikacja wyjtkw ma nastpujce waciwoci:


gwarantuje, e funkcja bdzie zgasza jedynie wyjtki wyszczeglnione

w specyfikacji (czsto adne);


umoliwia kompilatorowi optymalizacj opart na informacji, e zgaszane

bd tylko wyjtki wyszczeglnione w specyfikacji (czsto adne).


Oczekiwania te ponownie s tylko pozornie zblione do rzeczywistoci. Przyjrzyj si
ponownie fragmentowi kodu z przykadu 13.2(b):
// Przykad 13.2(b). Powtrka i dwa potencjalne kamstewka
//
?
int Gunc() throw();
// Nie zgasza adnych wyjtkw
int Hunc() throw(A,B);
// Moe zgasza jedynie wyjtki typu A i typu B ?

Czy komentarze te s poprawne? Nie do koca. Funkcja Gunc moe zgosi wyjtek,
a funkcja Hunc moe zgosi wyjtek innego typu ni A lub B! Kompilator moe jedynie
zagwarantowa, e bezlitonie potraktuje takie funkcje, jeli zrobi co takiego przy
czym najczciej rwnie bezlitonie potraktuje take cay program.
Poniewa funkcje Gunc i Hunc mog w rzeczywistoci zgasza wyjtki, ktrych zgasza
nie powinny, kompilator nie tylko nie moe zaoy, e taka sytuacja si nie zdarzy, ale
musi take peni rol ochroniarza, ktry dba o to, aby przechwyci wszystkie niepoprawnie zgoszone wyjtki. Jeli tak si stanie, musi zosta wywoana funkcja unexpected.
Najczciej przerywa ona dziaanie programu. Dlaczego? Poniewa istniej tylko dwa
sposoby zakoczenia funkcji unexpected, z ktrych aden nie wie si z wywoaniem instrukcji return:
Moliwe jest zgoszenie innego wyjtku znajdujcego si w specyfikacji

wyjtkw. Jeli tak si stanie, wyjtek przekazywany jest tak jak w normalnej
sytuacji. Pamitaj jednak, e funkcja unexpected jest globalna w caym

96

Rozdzia 2. Zagadnienia i techniki zwizane z bezpieczn obsug wyjtkw

programie istnieje tylko jedna jej wersja. Jest bardzo mao prawdopodobne,
aby taka funkcja globalna wykonywaa odpowiednie dziaania we wszystkich
przypadkach, w wyniku czego program przechodzi do funkcji terminate,
nie przechodzi przez blok catch i koczy dziaanie.
Zgosi (ten sam lub inny) wyjtek, ktrego take nie ma w specyfikacji

wyjtkw. Jeli oryginalna funkcja umoliwia zgaszanie wyjtkw typu


bad_exception, wtedy przekazany zostanie wyjtek wanie tego typu. Jeli
nie, program przechodzi do funkcji terminate, nie przechodzi przez blok catch

Poniewa naruszenie specyfikacji wyjtkw przewanie wie si z zakoczeniem dziaania programu, usprawiedliwione jest nazwanie tej sytuacji bezlitosnym potraktowaniem programu.
Wczeniej miae okazj zobaczy, jakie moliwoci wiele osb przypisuje specyfikacji
wyjtkw. Poniej znajduje si poprawiona wersja tych oczekiwa, ktra dokadniej
przedstawia rzeczywiste moliwoci [sic]6:
Gwarantuje Wymusza w czasie wykonania programu, e funkcja bdzie

zgasza jedynie wyjtki wyszczeglnione w specyfikacji (czsto adne).


Umoliwia lub uniemoliwia kompilatorowi optymalizacj opart na

informacji, e zgaszane bd tylko wyjtki wyszczeglnione w specyfikacji


(czsto adne) sprawdzeniu, czy wyszczeglnione w specyfikacji wyjtki
rzeczywicie zostay zgoszone.
Aby zrozumie, co robi kompilator, przyjrzyj si poniszemu fragmentowi kodu, ktry
przedstawia ciao jednej z przykadowych funkcji:
// Przykad 13.4(a)
//
int Hunc() throw(A,B) {
return Junc();
}

Kompilator musi wygenerowa kod taki jak poniej. Szybko dziaania takiego kodu
jest zwykle porwnywalna z kodem, ktry napisaby samodzielnie (oprcz czasu spdzonego na pisaniu):
// Przykad 13.4(b). Wersja przykadu 13.4(a) rozwinita przez kompilator
//
int Hunc()
try {
return Junc();
}
catch( A ) {
throw;
}
catch( B ) {
throw;

Tak, to taki art.

Zagadnienie 13. Specyfikacja wyjtkw z praktycznego punktu widzenia

97

}
catch( ... ) {
std::unexpected(); // Nie wywouje return! Jeli masz szczcie, zgasza wyjtek typu A
lub typu B
}

Teraz moesz atwiej zrozumie, dlaczego zamiast optymalizacji kodu opartej na zaoeniu, e zgaszane s jedynie okrelone typy wyjtkw, dzieje si co wrcz przeciwnego. Kompilator musi wykona wicej zada, aby wymusi zgaszanie jedynie okrelonych wyjtkw w czasie wykonywania programu.

Zakres specyfikacji wyjtkw


Wikszo osb jest zaskoczona, kiedy dowiaduje si, e specyfikacja wyjtkw moe
wiza si z wikszymi kosztami wykonywania programu. Jedna z przyczyn takiego
stanu rzeczy zostaa wanie wystarczajco obszernie wyjaniona. Specyfikacja wyjtkw powoduje narzut zwizany z niejawnym generowaniem blokw try i catch, chocia
w przypadku wydajnych kompilatorw moe by on niezauwaalny.
Istniej przynajmniej dwie inne przyczyny, dla ktrych specyfikacja wyjtkw moe
negatywnie wpywa na wydajno programu:
Niektre kompilatory automatycznie przeksztacaj funkcje inline ze

specyfikacjami wyjtkw na zwyke funkcje, stosuj take inne heurystyki,


na przykad przeksztacanie funkcji inline, ktre zawieraj wicej ni okrelon
liczb zagniedonych wyrae lub ptle w dowolnej postaci.
Niektre kompilatory w ogle nie optymalizuj kodu zwizanego z wyjtkami
i dodaj generowane bloki try i catch nawet wtedy, kiedy funkcja na pewno

nie bdzie zgasza wyjtkw.


Pomijajc wydajno czasu wykonania, specyfikacja wyjtkw moe wyduy czas
tworzenia programu, poniewa zwiksza zalenoci. Na przykad usunicie typu ze
specyfikacji wyjtkw funkcji wirtualnej w klasie bazowej to szybki i prosty sposb
na problemy w wielu klasach pochodnych (jeli tylko szukasz takiego sposobu). Moesz
go wyprbowa w pracy w pitkowe popoudnie i przeprowadzi ankiet na temat liczby
listw elektronicznych z pogrkami, ktre bd czeka na Ciebie w poniedziaek.
Dlatego kolejne pytanie brzmi:
5. Kiedy warto doda do funkcji specyfikacj wyjtkw? Co powoduje, e decydujesz

si doda t waciwo, a co nie?


Poniej znajduj si prawdopodobnie najlepsze wskazwki sformuowane na podstawie
dotychczasowych dowiadcze ze specyfikacj wyjtkw:
Wniosek 1: Nigdy nie pisz specyfikacji wyjtkw.
Wniosek 2: Moesz ewentualnie pisa puste specyfikacje wyjtkw, ale odradzabym
nawet to.

98

Rozdzia 2. Zagadnienia i techniki zwizane z bezpieczn obsug wyjtkw

Dowiadczenia twrcw biblioteki Boost ze specyfikacjami niezgaszajcymi wyjtkw


dla funkcji, ktre nie s inline, to jedyny przypadek, w ktrym specyfikacje wyjtkw
mog na niektrych kompilatorach przynosi pewne korzyci. Stwierdzenie to nie
jest moe olniewajce, ale stanowi uyteczn wskazwk, jeli chcesz tworzy przenony kod, ktry mona skompilowa na wicej ni jednym kompilatorze.
W praktyce jest jeszcze gorzej, poniewa okazuje si, e popularne implementacje
rni si w sposobie obsugi specyfikacji wyjtkw. Przynajmniej jeden z kompilatorw jzyka C++ (wszystkie wersje kompilatora Microsoftu do czasu wydania ksiki,
czyli do wersji 7.1 z roku 2003) przetwarzaj specyfikacje wyjtkw, ale ich nie wymuszaj, przez co redukuj je do specjalnych komentarzy. Czekaj, to jeszcze nie koniec.
Jednoczenie istniej poprawne optymalizacje, ktre kompilator wykonuje poza funkcj,
oparte na wymuszaniu specyfikacji wyjtkw wewntrz kadej funkcji i kompilator
Microsoftu w wersjach 7.x wykonuje je. Pomys polega na tym, e jeli funkcja prbuje
zgosi wyjtek, ktrego nie powinna, wtedy wewntrzna funkcja zatrzymuje program
i sterowanie nigdy nie wraca do funkcji wywoujcej. Poniewa jednak sterowanie wraca
do funkcji wywoujcej, w kodzie wygenerowanym w miejscu wywoania mona zaoy, e aden wyjtek nie zosta zgoszony, co pozwala wyeliminowa zewntrzne
bloki try i catch.
W przypadku kompilatorw, ktre nie wymuszaj przestrzegania specyfikacji wyjtkw, ale polegaj na niej, znaczenie pustej specyfikacji throw() zmienia si. Zamiast
sprawd list, zatrzymaj mnie, jeli zgosz niedozwolony wyjtek oznacza to teraz:
zaufaj mi, za, e nigdy nie zgosz wyjtku i wykonaj optymalizacj. Bd wic
ostrony. Jeli zdecydujesz si uy pustej specyfikacji wyjtkw, przeczytaj dokumentacj kompilatora, ktrego uywasz, aby sprawdzi, w jaki sposb kompilator j
obsuguje. Moesz by powanie zaskoczony. Bd ostrony, kieruj uwanie.

Podsumowanie
W skrcie nie musisz si martwi o specyfikacje wyjtkw. Nawet specjalici tego
nie robi.
Poniej, w nieco mniejszym skrcie, przedstawione s najwaniejsze zagadnienia:
Specyfikacja wyjtkw moe powodowa nieoczekiwane spadki wydajnoci,

wynikajce na przykad z przeksztacania funkcji inline ze specyfikacjami


wyjtkw na zwyke funkcje.
Bd czasu wykonania unexpected nie zawsze jest tym, czego chciaby

w przypadku typw bdw przechwytywanych za pomoc specyfikacji


wyjtkw.
Nie moesz utworzy uytecznej specyfikacji wyjtkw dla szablonw funkcji,

poniewa zwykle nie wiesz, jakie wyjtki mog zgasza typy, na ktrych
funkcja ta operuje.
Kiedy przedstawiaem te zagadnienia jako cz wikszego wykadu na niedawnej konferencji, zapytaem, kto z okoo 100 osb kadorazowo uywa specyfikacji wyjtkw.
Okoo poowa podniosa rce. Wtedy jaki artowni z tyu sali powiedzia (cakiem

Zagadnienie 13. Specyfikacja wyjtkw z praktycznego punktu widzenia

99

susznie), e powinienem take zapyta, ile z tych osb w pewnym momencie rezygnuje ze specyfikacji wyjtkw. Zapytaem. Zgosio si mniej wicej tyle samo osb, co
poprzednio. Ta sytuacja mwi sama za siebie. Czoowi projektanci bibliotek w korporacji Boost maj podobne dowiadczenia i dlatego ich strategia dotyczca pisania specyfikacji wyjtkw sprowadza si do prostego nie rb tego [BoostES].
To prawda, e wiele osb o dobrych intencjach domagao si wczenia do jzyka specyfikacji wyjtkw i dlatego znalazy si one w standardzie. Przypomina mi to sympatyczny wierszyk, ktry pierwszy raz przeczytaem okoo 15 lat temu, kiedy pojawi
si w listach elektronicznych w czasie ferii zimowych. Sowa piewane s na melodi
Wrd nocnej ciszy, a wierszyk zatytuowany by Wrd nocnej implementacji lub
Wrd nocnego kryzysu. Opowiada on o dowiadczonym programicie, ktry lczy
po nocach w czasie ferii, aby zdy przed terminem. W tym celu dokonuje wielu cudw,
ktre pozwalaj utworzy dziaajcy system doskonale speniajcy wymagania. Niestety, na koniec zostaje na lodzie, o czym mwi cztery ostatnie linijki wierszyka:
Program gotowy, skoczone testy,
nawet poprawki klienta s w nim.
Uytkownik jednak prycha i umiecha si krzywo z cicha:
O to prosiem, lecz nie tego chc, o to prosiem, lecz nie tego chc.
To samo mona powiedzie, przygldajc si dotychczasowym dowiadczeniom ze
specyfikacj wyjtkw. Swego czasu waciwo ta przedstawiaa si obiecujco
i jest dokadnie tym, czego niektre osoby oczekiway.
Uwaaj, czego sobie yczysz, bo moe si speni.

You might also like