Dnes budeme pokraΦovat v²jimkami, kterΘ jsme naΦali v minulΘm dφlu. Probereme podrobn∞ji Üφ°enφ v²jimek jazyka C++, standardnφ v²jimky knihovny jazyka C++ a °ekneme si v jak²ch funkcφch ze standardnφ knihovny mohou vznikat.
V minulΘm dφlu jsme si p°edvedli, jak pomocφ klφΦovΘho slova try m∙₧eme vyvolat takzvanou synchronnφ v²jimku. Dßle vφme, ₧e blok catch (tzv. handler) m∙₧e tuto v²jimku zachytit a n∞jak²m zp∙sobem na ni zareagovat. Provedenφm bloku catch v²jimku takzvan∞ obslou₧φme. Z nßzvu synchronnφ v²jimka lze vytuÜit, ₧e existujφ i v²jimky asynchronnφ. Asynchronnφ (hardwareovß) v²jimka m∙₧e b²t generovßna libovolnou instrukcφ. V jazyce C++ nelze jako v²jimku zachytit nap°φklad klßvesovou kombinaci Ctrl + Break, kterß vyvolß p°eruÜenφ.
Nynφ si povφme n∞co podrobn∞ji o Üφ°enφ v²jimky. Po vzniku v²jimky dojde k p°eskoΦenφ °ßdk∙ po p°φkazu throw a program zaΦne hledat handler, kter² by odpovφdal typu objektu, kter² byl pomocφ p°φkazu "vyhozen". AvÜak t∞sn∞ p°ed opuÜt∞nφm bloku try, ve kterΘm v²jimka vznikla, dojde k zavolßnφ destruktor∙ vÜech lokßlnφch instancφ objektov²ch typ∙ v pam∞¥ovΘ t°φd∞ auto. Pokud funkce nalezne odpovφdajφcφ handler, pak do n∞j vstoupφ a je na handleru, co s v²jimkou ud∞lß - m∙₧e ukonΦit program, vypsat chybovΘ hlßÜenφ atd. Pokud handler program neukonΦφ, program pokraΦuje za ukonΦovacφ zßvorkou tohoto handleru. V minulΘm dφlu jsme si ale ukßzali, ₧e za blokem try m∙₧e nßsledovat vφce handler∙. V tom p°φpad∞ pak program p°eskoΦφ vÜechny ostatnφ handlery a pokraΦuje za ukonΦovacφ zßvorkou poslednφho handleru. Pokud je hledßnφ handleru ne·sp∞ÜnΘ, pak dojde k rozÜφ°enφ v²jimky do vyÜÜφho bloku. VyÜÜφ blokem m∙₧e b²t nap°. funkce, kterß zavolala funkci, ve kterΘ vznikla v²jimka. P°φkazy za volßnφm funkce v tomto nad°φzenΘm bloku se op∞t neprovedou a dojde k novΘmu hledßnφ handleru. Pokud se program dostane na nejvyÜÜφ ·rove≥, kterou je funkce main(), pak dojde k zavolßnφ funkce terminate(), kterß program okam₧it∞ ukonΦφ. D∙le₧itΘ je, ₧e p°i opouÜt∞nφ blok∙ se provßdφ ·klid, jak bylo uvedeno. V p°φpad∞ zavolßnφ terminate() k niΦemu takovΘmu nedojde. Pokud je program ukonΦen touto funkcφ, mluvφme o tzv. neoÜet°enΘ v²jimce. Dost bylo teorie, uka₧me si troÜku slo₧it∞jÜφ p°φklad:
#include <iostream.h>
#include <math.h>
class Vysl {
private:
double m_arRes[2];
public:
Vysl(double _fVysl1, double _fVysl2)
{
m_arRes[0] = _fVysl1;
m_arRes[1] = _fVysl2;
}
double GetVysl1() { return m_arRes[0]; }
double GetVysl2() { return m_arRes[1]; }
};
double Odmocnina(double a)
{
try
{
if(a < 0) {
throw "Nelze
spocitat odmocninu ze zaporneho cisla";
}
return sqrt(a);
}
catch(char *)
{
throw;
}
catch(...)
{
cout << "Nastala neznama chyba
zachycena funkci Odmocnina" << endl;
}
}
void ZadejCislo()
{
try {
double a, b, c, odmdiskr;
cout << "Zadej koef kvadr. rovnice (ax^2 + bx + c) : " << endl;
cout << "\ta = ";
cin >> a;
cout << "\tb = ";
cin >> b;
cout << "\tc = ";
cin >> c;
cout << "Reseni : " << endl;
odmdiskr = Odmocnina(b*b - 4*a*c);
if(0 == odmdiskr)
{
throw (-b) / (2*a);
}
else
{
throw(Vysl((-b + odmdiskr) / (2*a), (-b - odmdiskr) / (2*a)));
}
}
catch(char *e)
{
cout << "Nema reseni v oboru realnych cisel (nastala chyba " << e << ")" <<
endl;
}
catch(double vysl)
{
cout << "Ma jedno reseni a to x1 = " << vysl << endl;
}
catch(Vysl& e)
{
cout << "Ma dve reseni a to x1 = " << e.GetVysl1() << " a x2 = " << e.GetVysl2()
<< endl;
}
catch(...)
{
cout << "Nastala nejaka jina chyba" << endl;
}
}
int main(int argc, char* argv[])
{
ZadejCislo();
return 0;
}
Nynφ si projdeme vÜechny t°i mo₧nosti, kterΘ mohou p°i b∞hu programu nastat. P°eskoΦφme t∞lo funkce ZadejCislo(), proto₧e v n∞m nenφ nic zajφmavΘho krom∞ klφΦovΘho slova try, kterΘ uvozuje pokusn² blok ve kterΘm se zachytßvajφ v²jimky :
1. NejjednoduÜÜφ p°φpad, kdy u₧ivatel zadß rovnici, kterou nelze vy°eÜit v oboru reßln²ch Φφsel (tedy diskriminant je zßpornΘ Φφslo, ze kterΘho nelze spoΦφtat odmocninu):
Ve funkci odmocnina dojde ke spln∞nφ podmφnky a tedy je vyvolßna v²jimka typu char* a zaΦne hledßnφ handleru, kter² by tuto v²jimku obslou₧il. P°i v²stupu z bloku try by se uvolnily lokßlnφ prom∞nnΘ vΦetn∞ zavolßnφ destruktor∙, ale zde ₧ßdnΘ nejsou. Handler je nalezen, tak₧e dojde k provedenφ jeho t∞la. V t∞le handleru se nachßzφ novinka a tou je samostatnΘ klφΦovΘ slovo throw. To zp∙sobφ, ₧e v²jimka je dßle Üφ°ena do nad°azenΘho bloku. To nßm dßvß mo₧nost v²jimku ΦßsteΦn∞ oÜet°it (nap°. uzav°φt n∞kterΘ soubory apod.) a Üφ°it ji dßl. Handler ... (ellipsis) je uveden jen aby bylo vid∞t, ₧e blok try m∙₧e mφt vφce handler∙. Z tΘto funkce se ale jinß v²jimka Üφ°it nem∙₧e. Vra¥me se k naÜφ Üφ°φcφ se v²jimce typu char*. Ta se rozÜφ°ila do bloku try kter² zavolal funkci Odmocnina(). DalÜφ p°φkazy za funkcφ Odmocnina() se neprovedou a zaΦne se hledat handler pro obsluhu v²jimek typu char*. Ten je nalezen a vypφÜe °et∞zec, ₧e kvadratickß funkce nemß °eÜenφ a d∙vod, kter²m je, ₧e nem∙₧eme odmocnit zßpornΘ Φφslo. Proto₧e handler neukonΦil program, program pokraΦuje za zßvorkou poslednφho handleru ve funkci ZadejCislo() a dojde tedy k ukonΦenφ tΘto funkce a nßvratu do funkce main().
2. Diskriminant rovnice je 0, pak mß rovnice pouze jedno °eÜenφ:
Funkce Odmocnina() prob∞hne v tomto p°φpad∞ bez vyvolßnφ v²jimky, ale v²jimka je vyvolßna o dva °adky pozd∞ji. Nynφ je v²jimka typu double a proto se hledß handler, kter² je schopn² tento typ obslou₧it. Prvnφ Üanci dostane handler (char *e), ale typ neodpovφdß a tak je p°eskoΦen, druh² handler vyhovuje a tak program vypφÜe °eÜenφ kvadratickΘ rovnice. Zde je nutnΘ poznamenat, ₧e pokud bychom na prvnφm mφst∞ uvedli handler ..., pak by ₧ßdn² jin² handler nem∞l Üanci vzniklou v²jimku zachytit. Program konΦφ stejn∞ jako v bod∞ 1.
3. Diskriminant je kladn² a rovnice mß pak dva ko°eny:
Funkce Odmocnina() op∞t prob∞hne sprßvn∞, ale v²jimka je vyvolßna o pßr °ßdk∙ dßle. KlφΦovΘ slovo throw nßm umo₧≥uje "vyhodit" i objektov² typ, Φeho₧ takΘ vyu₧ijeme. Zkonstruujeme instanci t°φdy Vysl, kterß obsahuje ob∞ °eÜenφ rovnice. Op∞t dojde k hledßnφ handleru a je nalezen handler s typem reference na t°φdu Vysl. Tφm dojde k p°edßnφ adresy, co₧ je v²hodnΘ u rozm∞rn∞jÜφch objekt∙. Ostatnφ handlery jsou op∞t p°eskoΦeny a program konΦφ stejn∞ jako v p°edchozφch bodech.
Je nutnΘ si uv∞domit, ₧e v²Üe uveden² p°φklad je jen pro v²ukovΘ ·Φely, v reßlu jde samoz°ejm∞ napsat jako pßr if p°φkaz∙. Obsluha v²jimek toti₧ nenφ zadarmo a tak je nutnΘ v₧dy zvß₧it, jestli nelze program napsat lΘpe.
Jak u₧ jsme si uvedli, handler se vybφrß podle typu v²jimky. Typ v²jimky je urΦen typem hodnoty, kterou jsme p°edali p°i vyvolßnφ v²jimky throw.
- V²jimku m∙₧eme zachytit bu∩ p°φmo pomocφ typu v²jimky nebo pomocφ reference (nap°. Typ& e) na typ v²jimky.
- Jestli₧e vyhozen²m typem je t°φda, kterß mß rodiΦovskou t°φdu/t°φdy, pak ji lze zachytit i handlerem, kter² bere jako typ rodiΦovskou t°φdu. TakΘ ji lze zachytit pomocφ reference na zßkladnφ t°φdu. Poznamenejme jen, ₧e pokud je objekt zachycen pomocφ reference, pak nenφ kopφrovßn, ale je p°φmo svßzßn s objektem, kter² jsme specifikovali p°i pou₧itφ throw. Jinak je to lokßlnφ kopie.
- Pokud uvedeme rodiΦovskou t°φdu objektu p°ed odvozenou t°φdou v seznamu handler∙, pak p°i vyhozenφ odvozenΘ t°φdy dojde k jejφmu zachycenφ handlerem pro rodiΦovskou t°φdu, aΦkoliv dßle existuje handler p°esn∞ pro tento typ
- handler s parametrem TypA* zachytφ i v²jimku typu TypB*, ale jen v p°φpad∞, ₧e existuje standardnφ konverze ukazatele typu TypA* na TypB*
- handler ... (ellipsis) zachytφ vÜechny typy v²jimek. Je proto nutnΘ ho uvΘst jako poslednφ
Nap°φklad typ char* je zachycen handlerem na typ void*, nebo¥ tato konverze je automatickß. Obrßcen∞ to neplatφ. Dßle se neprovßd∞jφ t°eba konverze typu char na int, aΦkoliv jinde jsou ·pln∞ b∞₧nΘ.
JeÜt∞ je dobrΘ zmφnit, ₧e pokud se hledß handler pro v²jimku, nesmφ vzniknout v²jimka jinß (na stejnΘ ·rovni). To je d∙le₧itΘ p°edevÜφm, pokud se p°i opouÜt∞nφ blok∙ try volajφ destruktory lokßlnφch objekt∙. Z toho d∙vodu se doporuΦuje psßt destruktory tak, aby se z nich nemohly Üφ°it v²jimky.
Pokud se v²jimka Üφ°φ z ji₧ napsanΘ funkce a na projektu pracuje vφce programßtor∙, nebo nenφ p°φstup ke zdrojovΘmu k≤du funkce, pak je vhodnΘ uvΘst jakΘ v²jimky se mohou z danΘ funkce rozÜφ°it. Samoz°ejmostφ je, ₧e by to m∞lo b²t uvedenΘ v komentß°i k funkci, ale jazyk C++ nabφzφ nßsledujφcφ zßpis:
NavratHodnota JmenoFunkce(Parametry) throw (seznam v²jimek, kterΘ se z funkce mohou Üφ°φt);
Pokud nenφ u hlaviΦky funkce uveden seznam v²jimek, kterΘ se z nφ mohou rozÜφ°it, pak se p°edpoklßdß, ₧e se z nφ m∙₧e Üφ°it jakßkoliv v²jimka. Pokud jsou uvedeny, pak se sm∞jφ Üφ°it jen uvedenΘ typy v²jimek. Proto₧e typem v²jimky m∙₧e b²t i objektov² typ, pak jsou povoleni i potomci tΘto t°φdy. Poslednφ mo₧nostφ je uvedenφ klφΦovΘho slova throw bez seznamu v²jimek. Potom se z danΘ funkce nesmφ Üφ°it v²jimka ₧ßdnß. Nßsledujφ p°φklady:
int VsechnyVyjimky(); // standardni zapis, muze se sirit cokoliv
int ZadnaVyjimka() throw(); // nesmi se sirit zadna vyjimka mimo telo funkce
int VybraneVyjimka() throw(int, Vysl); // mohou se sirit pouze uvedene vyjimky
To, ₧e se z funkce nesmφ Üφ°it v²jimka neznamenß, ₧e v nφ v²jimka nem∙₧e vzniknout. Jen musφ b²t oÜet°enß uvnit° tΘto funkce.
Pokud se z funkce rozÜφ°φ v²jimka, kterß nemß "povolenφ" se z funkce Üφ°it, pak zp∙sobφ zavolßnφ funkce unexpected(), kterß implicitn∞ zavolß funkci terminate() a ta, jak u₧ vφme, ukonΦφ program. Podobn∞ jako funkci terminate(), m∙₧eme p°edefinovat i funkci unexpected() a to pomocφ funkce set_unexpected().
P°ekladaΦ C++ obsa₧en² ve v²vojovΘm prost°edφ Visual Studio .NET firmy Microsoft bere mo₧nost, ₧e se z funkce m∙₧e rozÜφ°it v²jimka urΦitΘho typu stejn∞, jako ₧e se z tΘto funkce m∙₧e Üφ°it v²jimka jakΘhokoliv typu.
Standardnφ knihovna jazyka C++ nßm nabφzφ k vyu₧itφ hierarchii t°φd v²jimek. Pro vyu₧itφ t∞chto t°φd je nutnΘ vlo₧it do programu soubor <stdexcept> (opravdu bez p°φpony .h). P°edkem tΘto hierarchie je t°φda exception, kterß je definovßna v hlaviΦkovΘm souboru <exception>.
Objektov² typ exception je souΦßstφ prostoru jmen std a proto bychom p°i p°φstupu k n∞mu a ke standardnφm, od n∞j odvozen²m, v²jimkßm m∞li pou₧φt zßpis std::exception. Od t°φdy exception d∞dφ vÜechny odvozenΘ t°φdy virtußlnφ metodu what(), kterß vypφÜe °et∞zec obsahujφcφ chybu. Od t°φdy exception jsou odvozeny nap°φklad t°φdy runtime_error a logic_error, kterΘ majφ konstruktory s parametrem typu string, kter² umo₧≥uje zadat °et∞zec s d∙vodem chyby.
Pokud budeme zachytßvat v²jimky ze standardnφ knihovny handlerem catch(std::exception& e), potom zachytφme vÜechny t°φdy z tΘto hierarchie. Zavolßnφm e.what() uvnit° handleru dostaneme informaci o vzniklΘ v²jimce.
V²jimky typ∙ z tΘto hierarchie vyvolßvajφ n∞kterΘ funkce a metody objektov²ch typ∙ ze standardnφ knihovny jazyka C++.
Zde si zmφnφme pßr funkcφ, kterΘ zp∙sobφ v p°φpad∞ problΘmu vyvolßnφ v²jimky.
- v C++ lze za b∞hu programu urΦovat typ instance pomocφ operßtoru typeid. Tento operßtor p°i nesprßvnΘm pou₧itφ, nap°. kdy₧ se sna₧φme zjistit informace o typu, na kter² ukazuje pointer s hodnotou NULL, vyvolß v²jimku bad_typeid:
#include <iostream.h>
#include <exception>
#include <typeinfo>
class A { virtual f() {}; };
int main () {
try {
A* a = NULL;
typeid (*a);
}
catch (std::exception& e)
// chytame vse
{
cout << "Exception: " << e.what();
}
return 0;
}
Program vypφÜe chybovou hlßÜku jen v p°φpad∞, ₧e je to ladicφ (debug) verze. Ve finßlnφ (release) vypφÜe chybu Abnormal program termination.
- pokud se operßtoru new nepoda°φ p°id∞lit pam∞¥, pak vyvolß v²jimku typu bad_alloc
- v²jimka ios_base::failure (ios_base je prostor jmen) m∙₧e b²t vyvolßna v p°φpad∞ ne·sp∞chu vstupnφ nebo v²stupnφ operace objektovΘho datovΘho proudu. ObjektovΘ proudy ale negenerujφ v²jimky implicitn∞, je nutnΘ zavolat funkci ios_base::exceptions()
- n∞kterΘ operace p°etypovßnφ pomocφ operßtoru dynamic_cast mohou vyvolat v²jimku bad_cast.
V dneÜnφm dφlu jsme dokonΦili obsluhu v²jimek C++. Na zßv∞r je nutnΘ °φct, ₧e tyto v²jimky nejsou jedin²mi, se kter²mi se lze setkat. Prost°edφ Windows nabφzφ tzv. strukturovanou obsluhu v²jimek, knihovna MFC (Microsoft Foundation Classes - soubor t°φd zjednoduÜujφcφ v²voj Win32 aplikacφ) nabφzφ svojφ vlastnφ ·pravu obsluhy v²jimek jazyka C++. TakΘ jsme se seznßmili s n∞kolika nov²mi pojmy jako nap°. prostory jmen. N∞kterΘ z t∞chto budou objasn∞ny v dalÜφm dφlu, kde se zam∞°φme i na n∞kterΘ specißlnφ operßtory p°etypovßnφ, identifikaci typu za b∞hu programu (RTTI) a mo₧nß i n∞co vφc.
PřφÜtě nashledanou.