Jazyk C++ II Výjimky
AR 2013/2014
Jazyk C++ II
Funkce abort Dříve byl obvyklý způsob zavolat metodu abort(). Metoda provádí okamžitě:
Vyprázdnění vyrovnávací paměti, Ukončení celého programu, Vrátí číslo nadřazeného procesu, Vypsala na chybový výstup (cerr) chybovou hlášku „Abnormal program termination“
Alternativně se využívala metoda exit(). AR 2013/2014
Jazyk C++ II
2
Dlouhé skoky Nástroj, který umožňuje opustit aktuální funkci a vrátit se do některého z dynamicky nadřízených bloků. Při vracení odstraní neobjektové automatické lokální proměnné. Pro práci s dlouhými skoky pracujeme s nástroji: Datový typ jmp_buf – pro zaznamenávání prostředí. Makro setjmp() - zaznamenává stav prostředí a místo kam se program vrátí po provedení dlouhého skoku. Podle návratové hodnoty zjistíme, jestli se jedná o návrat po uložení prostředí (0) nebo po dlouhém skoku. Funkcí long_jmp() – provedení dlouhého skoku. AR 2013/2014
Jazyk C++ II
3
Dlouhé skoky int i = setjmp(env); if(i == 0) { f(); //zpracovani normalniho navratu z f() } else { //zpracovani navratu po dlouhem skoku } Void f() { ... if(chyba) longjmp(env, 1); ... }
AR 2013/2014
Jazyk C++ II
4
Vrácení chybového kódu Pružnější způsob něž ukončení programu představuje označení problému pomocí návratové hodnoty funkce. Nejedná se v tomto případě o ukončení programu, ale pouze o signalizaci chyby. Návratová hodnota nenese obvykle komplexnější informace o chybě. Návratové hodnoty je možné ale ignorovat. Vracení výsledku pak bylo pomocí parametru funkce, který je buď ukazatel nebo reference. Přesnější povahu chyby se dá dozvědět z globální proměnné errno. Potřeba hlavičkového souboru <errno.h>. U vestavěných funkcí.
AR 2013/2014
Jazyk C++ II
5
Mechanismus výjimek Výjimka představuje zajímavý způsob, jak signalizovat programu, že nastala v určité funkci chyba. Řeší problémy, které byly řečeny u vracení chybového kódu jako návratový hodnoty. Ošetření výjimky se skládá ze tří částí: Vyvolání výjimky, Zachycení výjimky speciálním programem (handlerem), Pokusný blok.
Výjimka je vyvolána při vzniku nějakého problému. Příkaz spouštějící výjimku je v podstatě skokem. Používání výjimek výrazně zpomaluje program. AR 2013/2014
Jazyk C++ II
6
Vyvolání výjimky Vyvolání výjimky se provádí pomocí příkazu throw. Za ním následuje hodnota, například řetězec znaků nebo objekt, který označuje povahu výjimky { ... if(delitel == 0) throw DelitelJeNula(); }
AR 2013/2014
Jazyk C++ II
7
Zachycení výjimky Zachycení výjimky probíhám pomocí handleru. Handler začíná klíčovým slovem catch, za kterým v kulatých závorkách následuje deklarace typu označující typ výjimky na niž bude handler reagovat. catch (DelitelJeNula & ex) { ... } Pro zachycení všech výjimek se používá univerzální obsluha: catch (...) { ... }
AR 2013/2014
Jazyk C++ II
8
Pokusný blok try Identifikuje blok kódu, pro který budou výjimky aktivovány. Samotný pokusný blok je označen klíčovým slovem try, za kterým následuje blok programu uzavřený ve složených závorkách, pro nějž budou výjimky zaregistrovány. try { ... DeleniCisel(sumaPenez, pocetZamestnancu); ... }
AR 2013/2014
Jazyk C++ II
9
Implementace double DeleniCisel(const double &delenec, const double &delitel) { if(delitel == 0) throw DelitelJeNula(); return delenec / delitel; } try { ... DeleniCisel(sumaPenez, pocetZamestnancu); ... } catch (DelitelJeNula & ex) { std::cout << ex.what() << std::endl; } AR 2013/2014
Jazyk C++ II
10
Objekty jako výjimky Důležitou výhodou této skutečnosti je, že mezi různými funkcemi a situacemi, jež vytvářejí výjimky, můžeme rozlišovat podle jejich typů. I objekt může obsahovat informace, které mohou pomoci identifikovat podmínky způsobené vyvolanou výjimkou. V zásadě může tyto informace využívat k rozhodování o další činnosti i blok catch. Definici funkce můžeme kvalifikovat pomocí specifikace výjimky, jíž určíme, o jaký druh výjimky se jedná. Provedeme to tak, že připojíme specifikaci výjimky, která obsahuje klíčové slovo throw následovaného seznamem typů výjimek oddělených čárkami a uzavřených v závorkách. void prohozeniDvouPrvkuTabulky(const int &, const int &, const int &, const int &) throw(spatna_index);
AR 2013/2014
Jazyk C++ II
11
Objekty jako výjimky Tím dosáhneme dvou věcí: Překladač se dozví jaké typy výjimek funkce vyvolává. Upozorní každého, kdo bude prototyp číst, že tato konkrétní funkce vyvolává výjimky a připomene mu možnost použití pokusného bloku nebo handleru.
Jestliže funkce vyvolá nějaký jiný typ výjimky, bude program na to reagovat zavoláním funkce abort(). Funkce vyvolávající více typů výjimek mohou mít čárkou oddělený seznam jejich typů. AR 2013/2014
Jazyk C++ II
12
Specifikace výjimek v hlavičce funkce Jestli specifikaci neuvedeme, může se z funkce rozšířit jakákoliv výjimka. int najdiMaximalniHodnotu(); Pokud uvedeme prázdnou specifikaci throw(), nesmí z funkce žádná výjimka. int najdiMaximalniHodnotu() throw(); Pokud je neprázdná specifikace throw(), můžou se výjimky rozšířit pouze výjimky stejného typu nebo odvozené. int najdiMaximalniHodnotu() throw(exception);
AR 2013/2014
Jazyk C++ II
13
Třída exception
Novější překladače C++ mají výjimky včleněny přímo do jazyka. Např. hlavičkový soubor <exception>, který definuje třídu výjimek, kterou C++ používá jako základní třídu pro jiné třídy výjimek, jež podporují jazyk. Virtuální funkce what() vrací řetězec, jehož podstat je implementačně závislá.
class exception { public: exception(); exception(const char * const &message); exception(const exception &right); exception& operator=(const exception &right); virtual ~exception(); virtual const char *what() const; };
AR 2013/2014
Jazyk C++ II
14
Hierarchie standardních tříd pro přenos o výjimce
AR 2013/2014
Jazyk C++ II
15
Třída výjimek stdexcept Hlavičkový soubor <stdexcept> definuje některé další třídy výjimek, které jsou potomkem třídy exception. Třída definuje dvě oblasti tříd, které vycházejí ze tříd: logic_error a runtime_error.
Konstruktory obou tříd převezmou jako argument objekt typu string, který obsahuje data vrácená jako řetězec ve stylu C metodou what(). AR 2013/2014
Jazyk C++ II
16
Odvozené třídy Rodina třídy logic_error:
Využívané k popisu běžných logických chyb. Tyto chyby by se neměli v praxi vyskytnout.
Rodina třídy runtime_error:
Určená k popisu chyb, které mohou nastat při běhu programu. Jedná se o chyby, které nelze snadno předpovědět a nelze jim ani zabránit.
Odvozené třídy ze třídy logic_error se obecně týkají problémů, které lze pravděpodobně odstranit laděním programu, zatímco rodina výjimek runtime_error signalizuje spíše neočekávané problémy. Všechny tyto chybové třídy mají společnou obecnou charakteristiku. AR 2013/2014
Jazyk C++ II
17
Odvozené třídy z třídy logic_error domain_error
-
V případě, že je argument mimo definiční obor. Např. obor (-1, +1) u funkce, která předala vyšší argument funkci std::sin().
invalid_argument
-
Upozornění, že byla funkci předána jiná než očekávané hodnota. Např. pokud funkce očekává, že dostane řetězec, v němž jsou všechny znaky ‘0’ a ‘1’, a v řetězci se objeví jiné znaky.
-
length_error
-
out_of_bounds
AR 2013/2014
-
K indikaci skutečnosti, že pro požadovanou akci není k dispozici dostatek místa. Např. tuto výjimku vyvolá metoda append() třídy typu string, když by výsledný řetězec měl překročit maximální možnou délku. K indikaci indexových chyb. Např. Máme-li definovanou třídu jako pole, pak neplatný index pro dané polev operatoru operator()[] vyvolá tuto výjimku.
Jazyk C++ II
18
Odvozené třídy z třídy runtime_error range_error
-
Je-li výsledek mimo obor hodnot určité funkce. K přetečení ani k podtečení nemusí dojít.
underflow_error
-
Obecně existuje nejmenší nenulová hodnota, kterou ještě lze v tomto typu zobrazit. Chybu podtečení může také způsobit výpočet, jehož výsledek je menší než tohle číslo.
-
Overflow_error
AR 2013/2014
-
Chyba přetečení, která se může vyskytnout při výpočtech v pohyblivé řadové čárce, kdy výsledek výpočtu je větší než největší zobrazitelné číslo daného typu.
Jazyk C++ II
19
Výjimka bad_alloc V implementaci C++ si můžeme vybrat ze dvou možností, jak zpracovat problém s přidělováním paměti příkazem new a to, když nelze uspokojit požadavek na paměť.
Vrácení nulového ukazatele příkazem new – první a dříve jediná možnost. Vyvolání výjimky bad_alloc příkazem new.
Hlavičkový soubor
obsahuje deklaraci třídy bad_alloc, která je veřejně odvozená z třídy exception. Implementace může buď nabídnout pouze jednu možnost nebo nám dá na vybranou třeba pomocí speciálního přepínače. AR 2013/2014
Jazyk C++ II
20
Další výjimky Bad_cast
- Vyvolává ji operátor dynamic_cast, není-li možné provést požadované přetypování referencí. - V hlavičkovém souboru .
Bad_typeid
- Vyvolá ji operátor typeid, použijeme-li ho na ukazatel s hodnotou 0. - V hlavičkovém souboru .
Bad_exception
- Na tento typ mohou být za jistých okolností transformovány neočekávané výjimky. - V hlavičkovém souboru <exception>.
Ios_base::fauilure
- U datových proudů – běžně se nepoužívá. - Lze jim to ale přikázat pomocí metody exceptions() třídy basic_ios<>. - V hlavičkovém souboru .
AR 2013/2014
Jazyk C++ II
21
Informace o standardních výjimkách Všechny třídy ze standardní hierarchie, kromě třídy exception, mají konstruktor, který očekává jako parametr znakový řetězec. Do řetězce se vkládá podrobnější popis o vzniklé m problému. Tyto informace následně vrací virtuální metoda what() ve formě znakové řetězce Proto zachytávat výjimky odkazem
catch(exception &ex) { ... } AR 2013/2014
Jazyk C++ II
22
Nezachycená výjimka Jestliže výjimce není přiřazen žádný ze seznamu handlerů, který následují po určitém bloku try, výjimka se posune do příštího vyššího kontextu (do funkce nebo nadřazeného bloku try, který obklopuje ten blok try, který výjimku nezachytil). Tento postup pokračuje, dokud na určité úrovni není výjimce přiřazen handler. Pokud není přiřazen žádný handler, tak se jedná o nezachycenou výjimku. Nezpůsobuje okamžité ukončení programu. Místo toho zavolá funkci terminate(), která standardně volá funkci abort(). Chování funkce terminate() můžeme upravit pomocí registrace funkce, která bude vyvolána místo funkce abort(). K zaregistrování funkce slouží funkce set_terminate(), která je v hlavičkovým souboru <exception>.
AR 2013/2014
Jazyk C++ II
23
Neočekávaná výjimka
Výjimka, která je vyhozena funkcí, přestože není uvedena v její hlavičce. Jestliže se vyskytne neočekávaná výjimka, program zavolá funkci unexpected(). Funkce unexpected() zavolá funkci terminate(), která standardně volá funkci abort(). Stejně jako u nezachycených výjimek, tak zde máme funkci set_unexpected(), která upravuje chování funkce unexpected(). Tyto funkce jsou opět uloženy v hlavičkovém souboru <exception>.
void mojeUnexp() { std::cerr << "Neocekavana chyba\n"; exit(1); } int main(int argc, char ** argv) { set_unexpected(mojeUnexp); ... } AR 2013/2014
Jazyk C++ II
24
Shrnutí výjimek Ošetření výjimek by mělo být součástí programu a ne přidáno dodatečně. Při používání výjimek roste velikost programu a snižuje se rychlost vykonávání programu. Specifikace výjimek nepracují dobře se šablonami Vyvolávání různých druhů výjimek v závislosti na konkrétně použité specializaci.
Výjimky nenahrazují zcela používání návratových hodnot funkcí/metod jako indikátoru chybového stavu. AR 2013/2014
Jazyk C++ II
25
Literatura PRATA, Stephen. Mistrovství v C++. 3. aktualiz. vyd. Překlad Boris Sokol. Brno: Computer Press, 2007, 1119 s. ISBN 978-80-251-1749-1. VIRIUS, Miroslav. 1001 tipů a triků pro C. Vyd. 1. Brno: Computer Press, 2011, 451, xx s. ISBN 978-80-251-2941-8.
AR 2013/2014
Jazyk C++ II
26