17. rˇ´ıjna 2011, Brno ´ Pˇripravil: David Prochazka
´ Prace s vyjimkami ´ Programovac´ı jazyk C++
Obecneˇ
Strana 2 / 21
Jak se muˇ ˚ ze program zachovat pˇri chybeˇ Dˇr´ıve byl obvykl´y zpusob zavolat metodu abort. ˚ Metoda okamˇziteˇ ´ ´ ˇ vyprazdnila vyrovnavac´ ı pamet, ukonˇcila cel´y program, ´ ´ vratila cˇ ´ıslo nadˇrazeneho procesu, ´ sku o ukonˇcen´ı. vypsala na chybov´y v´ystup hlaˇ
Alternativneˇ se vyuˇz´ıvala metoda exit. ´ Nejedna´ se o elegantn´ı ˇreˇsen´ı, proto byl preferovan ´ mechanismus navratov´ ych hodnot. Metoda v tomto pˇr´ıpadeˇ neukonˇcuje program, jen signalizuje chybu. ´ Navratovou hodnotu je vˇsak moˇzne´ ignorovat. ´ ˇ s´ı Navratov a´ hodnota nenese obvykle komplexnejˇ ˇ informace o chybe.
Obecneˇ
Strana 3 / 21
Vyjimky ´ Vyj´ımka pˇredstavuje zaj´ımav´y zpusob, jak signalizovat ˚ programu, zˇ e v urˇcite´ entiteˇ nastala chyba. ˇ s´ı problemy ´ ˇ e´ u navratov´ ´ Reˇ zm´ınen ych hodnot. V´yjimky pouˇz´ıva´ ˇrada funkc´ı/metod ˇ (pˇr´ıstup pomoc´ı metody at() na m´ısto v ˇretezci). ´ Je vyvolana pomoc´ı pˇr´ıkazu throw. Hodnotou, ktera´ je jako vyj´ımka vyhozena muˇ ˚ ze b´yt ´ jednoduch´y datov´y typ, obvykle pouˇz´ıvame objekty. V´yjimky si muˇ ˚ zeme nadefinovat sami, pouˇz´ıt standardn´ı v´yjimky a v´yjimky od nich odvozene´ (doporuˇceno). ´ ana ´ ´ ´ jako try. V´yjimka je odchytav v bloku kodu oznaˇcenem ˇ sena je v bloku catch(). Reˇ Uˇzit´ı v´yjimek v´yrazneˇ zpomaluje program.
Obecneˇ
Strana 4 / 21
Implementace 1 2 3 4 5
try { // pokus o operaci potencialne vyhazujici vyj . } catch ( myException & e ) { // osetreni neplatneho pokusu }
V bloku catch je uveden odkaz na v´yjimku. Ve skuteˇcnosti se vˇsak jedna´ o odkaz na kopii. Puvodn´ ı v´yjimka obvykle v dobeˇ ˚ ´ ı jiˇz neexistuje. zpracovan´
Obecneˇ
Strana 5 / 21
ˇ ren´ı vyjimky Pˇr´ıklad osetˇ ´ u metody at() ´ ı v´yjimky, beh ˇ programu se Dojde-li v bloku try k vyvolan´ pˇreruˇs´ı a pˇrejde se k nejbliˇzsˇ ´ımu handleru v´yjimek – catch. 1 2
# include < exception > # include < string >
3 4 5 6 7 8 9 10 11 12 13
int main () { std :: string s ( " 1234 " ); try { s . at (5); std :: cout << " toto se nikdy nevypise " ; } catch ( exception & e ) { std :: cerr << " Nastala chyba " << std :: endl ; std :: cerr << e . what () << std :: endl ; } ...
´ ren´ı vyjimek Vytvaˇ ´
Strana 6 / 21
Pˇr´ıklad Vytvoˇrte tˇr´ıdu reprezentuj´ıc´ı zlomek, kter´y obsahuje atributy ´ ma´ metodu vydel, ˇ ktera´ vyhazuje citatel a jmenovatel. Dale ˇ ı nulou. V´yjimku reprezentujte vlastn´ı v´yjimku v pˇr´ıpadeˇ delen´ ´ tˇr´ıdou obsahuj´ıc´ı popis problemu (lze rozˇs´ıˇrit o dalˇs´ı informace – hodnoty cˇ itatele a jmenovatele, atp.). 1 2 3 4 5 6 7
class Vyjimka { private : std :: string m_popis ; public : void Vyjimka ( std :: string d ) { m_popis = d ; } std :: string vrat () { return m_popis ; } };
´ ren´ı vyjimek Vytvaˇ ´
Strana 7 / 21
ˇ ı vyhozen´ı vyjimek Rucn´ ´ ˇ Pokud ma´ m´ıt nekter a´ funkce moˇznost vyvolat v´yjimku, mus´ı to b´yt uvedeno v deklaraci funkce (metody). Pokud muˇ ˚ ze funkce ´ vracet v´yce typu˚ v´yjimek, mus´ı b´yt v zavorce vˇsechny. 1 2 3 4 5 6 7 8 9 10
class Zlomek { private : int m_citatel , m_jmenovatel ; public : Zlomek ( int citatel , jmenovatel ){ m_citatel = citatel ; m_jmenovatel = jmenovatel ; } double vydel () throw ( Vyjimka ); };
´ ren´ı vyjimek Vytvaˇ ´
Strana 8 / 21
Implementace metody vydel() 1 2 3 4 5 6 7 8 9
double Zlomek :: vydel () throw ( Vyjimka ){ if ( m_jmenovatel == 0) { Vyjimka v ( " Deleni nulou ve zlomku " ); throw v ; // lze vyhodit i nepojmenovanou instanci tridy // throw Vyjimka (" Popis problemu "); } return (( double ) m_citatel / m_jmenovatel ); }
´ Je durazn eˇ doporuˇcovano vyhazovat v´yjimky hodnotou a ˚ ´ odchytavat odkazem.
´ ren´ı vyjimek Vytvaˇ ´
Strana 9 / 21
Jak vyjimku ´ zpracujeme? 1
Zlomek * z = new Zlomek (10 ,0);
2 3 4 5 6 7 8 9 10 11 12 13
try { std :: cout << " 10/0= " << z - > vydel () << std :: endl ; std :: cout << " Pokud je delitel != 0 vypise se to " ; } catch ( Vyjimka & v ) { // pokud je vyhozena Vyjimka std :: cout << v . vrat () << std :: endl ; } catch ( jinaVyjimka & j ) { // pokud jina ... // opetovne uvolneni vyjimky pro zpracovani // specialni pripad , bezne nedelame ! throw ; }
´ ren´ı vyjimek Vytvaˇ ´
ˇ Odchycen´ı vsech vyjimek ´ ´ V zavorce jsou opravdu tˇri teˇcky. 1 2 3 4 5
try { // vyvolani vyjimky } catch (...) { // osetreni }
´ Nelze vˇsak identifikovat typ v´yjimky – druh problemu.
Strana 10 / 21
´ ı pˇr´ıpady Specialn´
Strana 11 / 21
ˇ ren´ı zrusen´ ˇ ı objektu – specialn´ ´ ı pˇr´ıpad! Osetˇ 1 2
// nˇ e kde v metodˇ e Trida * instance = new Trida ;
3 4 5 6 7
... // kod , ktery potencialne vyhodi vyjimku // tato vyjimka je vyslana mimo metodu ...
8 9 10
delete instance ; ...
Pokud bude vyhozena v´yjimka, objekt se nikdy nezruˇs´ı.
´ ı pˇr´ıpady Specialn´
Strana 12 / 21
ˇ ren´ı zrusen´ ˇ ı objektu – rˇesen´ ˇ ı Osetˇ 1
Trida * instance = new Trida ;
2 3 4 5
try { // kod , ktery potencialne vyhazuje vyjimku } catch ( exception & e ) {
6
delete instance ; // " pozastavenou " vyjimku je treba zase uvolnit throw ;
7 8 9 10
}
a) Nelze to oˇsetˇrit podm´ınkou? b) Metoda nesm´ı v´yjimku odchytit a zruˇsit bez toho, zˇ e by ´ vyˇreˇsila. Pokud problem ´ kter´y vyhozen´ı zpusobil problem ˚ ´ a, ´ mus´ı b´yt v´yjimka (znovu) vypuˇstena. ˇ setrvav
Hierarchie vyjimek ´
Strana 13 / 21
´ ren´ı vyjimek ˇ cnost ˇ Vytvaˇ ´ a dedi 1 2 3 4 5 6 7
class Vyjimka { private : std :: string m_popis ; public : Vyjimka ( std :: string popis ){ m_popis = popis ;} std :: string vratPopis () { return m_popis ; } };
8 9 10 11 12 13 14 15 16 17
class DeleniNulou : public Vyjimka { private : int m_cislo ; public : DeleniNulou ( std :: string t , int c ): Vyjimka ( t ){ m_cislo = c ; } int vratCislo () { return m_cislo ; } };
Hierarchie vyjimek ´
Strana 14 / 21
ˇ sen´ ˇ ı jejich odchycen´ı jiˇz bylo zm´ıneno ˇ Re 1
try {
2
// vyvolani v . } catch ( DeleniNulou & v ) { // osetreni } catch ( Vyjimka & v ) { // osetreni }
3 4 5 6 7
´ ´ v´yjimky Je nutne´ davat si pozor na poˇrad´ı v jakem ´ ame ´ odchytav (potomek muˇ ˚ ze vystupovat v roli pˇredka!). ´ ate ´ pˇredka, odchyt´ı se i potomek! Pokud nejdˇr´ıve odchytav
Standardn´ı vyjimky ´
Strana 15 / 21
Standardn´ı vyjimky ´ Sp´ısˇ e, neˇz definovat si vlastn´ı v´yjimky, je vhodne´ vyuˇz´ıvat ´ standardn´ıch v´yjimek, pˇr´ıpadneˇ odvozene. V´yjimky jsou odvozeny od tˇr´ıdy exception. ´ ı metodu what(). Tato tˇr´ıda obsahuje virtualn´ Z n´ı jsou odvozene´ logic error (chyby v logice programu) ˇ a runtime error (chyby zjistitelne´ aˇz za behu programu). Abychom mohli pouˇz´ıvat standardn´ı v´yjimky, je nutne´ naˇc´ıst stdexcept. ´ a´ ze standardn´ıch Pokud uˇzivateli nevyhovuje zˇ adn ´ hierarchie. v´yjimek, je vhodne´ odvodit vlastn´ı z teto ´ aby se programator ´ Proto je vhodne, nejdˇr´ıve dobˇre ´ seznamil se standardn´ımi v´yjimkami.
Standardn´ı vyjimky ´
Strana 16 / 21
Standardn´ı vyjimky ´ Z logic error se odvozuj´ı: ´ ana ´ domain error – asinu je pˇredav hodnota mimo interval -1, 1, ´ ´ invalid argument – funkci byla pˇredana neoˇcekavan a´ hodnota, length error – pro poˇzadovanou operaci nen´ı dostatek m´ısta (append), out of bounds – obvykle sˇ patn´y index.
Z runtime error se odvozuj´ı: ´ underflow error – pokud pˇri v´ypoˇctech v plovouc´ı cˇ arce ´ ı zobrazitelne, ´ vyjde cˇ ´ıslo, ktere´ je menˇs´ı, neˇz minimaln´ ˇ s´ı, neˇz maximaln´ ´ ı overflow error – v´ysledek cˇ ´ıslo je vetˇ ´ zobrazitelne, range error – v´ysledek je mimo obor hodnot.
A znich zase dalˇs´ı...
Standardn´ı vyjimky ´
Strana 17 / 21
´ Ukol Zkuste se zamyslet nad pˇr´ıkladem se zlomkem a upravit jej tak, ˇ aby podporoval nekterou z hierarchie standardn´ıch v´yjimek. ´ se pokuste vytvoˇrit vlastn´ı a zaˇradit ji do hierarchie. Dale
Standardn´ı vyjimky ´
Strana 18 / 21
Pˇr´ıklad standardn´ı vyjimky ´ 1 2 3 4 5 6
Trida * objekt ; try { objekt = new Trida ; } catch ( std :: bad_alloc & b ) { cout << " Vyhodil v´ y jimku " << endl ; }
´ ı v´yjimky lze zamezit a provest ´ ruˇcn´ı oˇsetˇren´ı: nebo vyvolan´ 1 2 3 4
objekt = new ( nothrow ) Trida ; if ( objekt == 0) cout << " nepovedlo se zaalokovat pamet " << endl ; }
ˇ avan ´ ´ ı Neocek e´ chovan´
Strana 19 / 21
ˇ ren´ı neodchycene´ vyjimky Osetˇ ´ Zavola´ se funkce terminate. Ta pokud nen´ı ˇreˇceno jinak, zavola´ abort. ´ ı lze modifikovat pomoc´ı set terminate: Toto chovan´ 1 2 3 4
void mojeTerminate () { cerr << " Neodchycena vyjimka " << endl ; // radeji vzdy ukoncit program }
5 6 7 8 9 10 11
int main () { set_terminate ( mojeTerminate ); throw 1; cout << " Nestane se " << endl ; return 0; }
ˇ avan ´ ´ ı Neocek e´ chovan´
Strana 20 / 21
ˇ ren´ı neocek ˇ avan ´ Osetˇ e´ vyjimky ´ ´ ´ ktera´ je vyhozena funkc´ı, Neoˇcekavan a´ v´yjimka je takova, pˇrestoˇze nen´ı uvedena v jej´ı hlaviˇcce. Zavola´ se funkce unexpected. Ta pokud nen´ı rˇeˇceno jinak, zavola´ terminate. ´ ı lze modifikovat pomoc´ı set unexpected: Toto chovan´ 1 2 3 4
void mojeUnexp () { cerr <<" Neocekavana chyba " << endl ; exit ( -10); // radeji vzdy ukoncit program }
5 6 7 8 9
int main () { set_unexpected ( mojeUnexp ); ... }
ˇ avan ´ ´ ı Neocek e´ chovan´
Strana 21 / 21
´ Poznamka ´ ı navratov´ ´ V´yjimky nenahrazuj´ı zcela pouˇz´ıvan´ ych hodnot ´ ´ funkc´ı/metod jako indikatoru chyboveho stavu. Jedna´ se o ˇ kter´y umoˇznuje ˇ ´ ´ urˇcit´y komplexn´ı doplnek, lepe pˇredavat ´ informace o tom co se vlastneˇ stalo a lepe take´ ˇreˇsit situace, kde muˇ ˚ ze doj´ıt ˇradeˇ ruzn´ ˚ ych chyb (najednou).