Obsluha výnimiek.

Slides:



Advertisements
Similar presentations
Ma.
Advertisements

El Alfabeto Con Vocabulario
Click on each of us to hear our sounds.
Las Vocales En Espanol.
HIRAGANA by number of strokes Images from:
PHONICS Repeat each sound. Blend the sounds. Read each word.
ma mu mi mo me pe pi pa pu po si sa so.
Sílabas con m,p,s tema 2. pe so ma si mu se.
MA. ME MI MO MU MÁ MÉ MÍ MÓ MŮ LA LE LI.
Dostupné z Metodického portálu ISSN: , financovaného z ESF a státního rozpočtu ČR. Provozováno Výzkumným ústavem pedagogickým v Praze.
Hľadanie motívov v reťazcoch DNA
Slovak HEROINE Comenius project
Example Bullet Point Slide
Inteligentné mapy Marek Doršic.
VOĽNE DOSTUPNÝ REFERENČNÝ MANAŽÉR
Renesancia a humanizmus
Prečo šimpanzy nevedia rozprávať?
Zálohovanie a archivácia
Operačné systémy Čo robí operačný systém ?
Java Collections Framework
BEZPEČNOSŤ DATABÁZ Bezpečnosť informačných systémov
O malej Aničke Anička sa hrá s loptami.
Domény a DNS.
RIZIKÁ PRI REALIZOVANÍ PROJEKTU
Vývoj a druhy počítačov
Prednáška 5. PACS SYSTÉMY
Úvod do jazyka C Algoritmizácia úloh 4.
DATABÁZOVÉ JAZYKY.
Bezpečnosť JAVA technológií
Yulia Šurinová "There is always a better way; it should be found."
Programovací jazyk programovací jazyk Pascal Delphi
Makrá v PowerPointe Joshua Lajčiak.
Schémy financovania v 7RP
Človek vo sfére peňazí ročník.
aktivácia Vladimír Hricka License Sales Specialist Microsoft Slovakia
7. prednáška 3. november 2003.
Protokoly HTTP a FTP.
Mgr. Valentína Gunišová
Princípy počítačov 1 LS 2002/2003
Využitie IKT na hodinách anglického jazyka
Výučba cudzích jazykov
Ako sme pristúpili k citlivej téme migrácie v našej škole
Informatika, I. ročník Gymnázium, Park mládeže 5, Košice
Skrutkovica na rotačnej ploche
Vlastnosti kvantitatívnych dát
Šifrovanie Dešifrovanie
Ako manažovať smartfóny z cloudu TechDays East 2014
Dvojrozmerné polia Kód ITMS projektu:
Lokálne príznaky vo farebných obrazoch
22. – OTVORENÝ PRÍSTUP
PRACOVNÉ PROSTREDIE PRI PRÁCI S POČÍTAČMI Z HĽADISKA ERGONÓMIE
Vysoko subjektívna prezentácia o používaní podcastov
Čo v súčasnosti vieme o HPV?
Heuristické optimalizačné procesy
Heuristické optimalizačné procesy
Zásady hygieny pri stolovaní
REACH 2018 Nájdite svojich spoluregistrujúcich a pripravte sa na spoločnú registráciu.
Ing. Anita Sáreníková/ Cvičenia z aplikovanej informatiky
Polymorfizmus.
4. Užívateľské prostredie UNIXu
Veľkosť trhu agentúrnych zamestnancov
De Bonových 6 klobúkov myslenia
Výnimky I, adresáre a súbory
Seminár č. 9 - osnova Metódy sieťového plánovania a riadenia:
Základné tematické okruhy
Interaktívna kniha a e-learningový systém pre deti - Opera nehryzie
8. prednáška 10. november 2003.
D Novinky v DSpace 6 Ivan Masár 6.
Využitie biomasy v environmentálnych biotechnológiách
TVORBA VIET A OTÁZOK a KRÁTKYCH ODPOVEDÍ
Presentation transcript:

Obsluha výnimiek

Osnova prednášky Chyby a výnimky Spracovanie výnimiek Výnimky pri alokácii pamäte Výnimky v konštruktoroch

Chyby a výnimky Pri vývoji a používaní programu sa môže vyskytnúť niekoľko druhov problémov: 1. Chyby v kóde – tieto chyby sa zachytia už v štádiu kompilácie, to znamená, že ak sa v programe vyskytujú, program sa ani nespustí 2. Logické chyby – program sa spustí, ale kvôli chybe programátora nefunguje správne 3. Výnimky – sú to problémy, ktoré nastanú počas behu programu a vznikajú v dôsledku vonkajších okolností - nevhodných dát (delenie nulou, pokus o čítanie neexistujúceho súboru..), hardvérových problémov (nedostatok pamäte, poškodený hardvér) a podobne Zatiaľ čo chyby (v kóde a logické) musí programátor odstrániť, výnimky môže len predvídať a môže zahrnúť do programu príkazy, ktoré sa vykonajú, ak výnimka nastane

Spracovanie výnimky Spracovanie výnimky v C++ je založené na troch kľúčových slovách: try, throw a catch (skús, oznám a zachyť) Blok try – obsahuje časť programu, v ktorej sa môže vyskytnúť výnimka. Môžeme ho chápať tak, že program skúsi danú časť programu vykonať a zisťuje, či pri tom nastane výnimka alebo nie. Príkaz throw – oznámi, že nastala výnimka (a aká). Ak sa nachádza priamo v bloku try, vykonávanie príkazov v bloku sa končí, ak sa nachádza vo vnútri nejakej funkcie, program z funkcie odchádza. Dôležitou vlastnosťou tohto príkazu je, že zabezpečuje korektný odchod z bloku alebo funkcie - zruší lokálne premenné, ktoré boli vytvorené, a zavolá deštruktory objektov. Funkcia catch – táto funkcia zachytí výnimku určitého typu. V jej tele je uvedené, čo sa má spraviť, ak daná výnimka nastala - môže ukončiť program, korektne obísť problém, alebo skúsiť napraviť chybu.

Spracovanie výnimky Syntax mechanizmu try-throw-catch je nasledujúca: { ... ak nastala výnimka throw výnimka; } catch(typ_výnimky výnimka) Samozrejme, v bloku try môže nastať niekoľko výnimiek, rovnakého alebo rôzneho typu. Takisto príkazov catch môže byť za blokom try ľubovoľne veľa, jeden pre každý možný typ výnimky. Príkaz throw musí byť spustený buď z bloku try alebo z nejakej funkcie, ktorá sa volá v tomto bloku. Výnimka môže byť premenná ľubovoľného dátového typu.

Spracovanie výnimky Príklad: Delenie komplexných čísel Pokúsime sa vydeliť dve komplexné čísla. V prípade, že nastane delenie nulou, program ohlási výnimku. Definujeme konštantu, ktorá bude indikátorom výnimky: #define Delenie_nulou 1 Hlavný program: KomplexneCislo x(1,2), y(3,-1),z; cout<<"Komplexne cisla: "; x.Vypis(); cout<<", "; y.Vypis(); try { if (y.A()==0 && y.B()==0) throw Delenie_nulou; z.ZmenA((x.A()*y.A()+x.B()*y.B())/(y.A()*y.A()+y.B()*y.B())); z.ZmenB((x.B()*y.A()-x.A()*y.B())/(y.A()*y.A()+y.B()*y.B())); }

Spracovanie výnimky Hneď za blokom try výnimku zachytíme: catch(int vynimka) { if (vynimka==Delenie_nulou) cout<<"Nulou delit neviem!"<<endl; return 1; } cout<<"Podiel tychto cisel: "; z.Vypis(); Pri daných číslach dostaneme výstup: Komplexne cisla: 1+2i, 3-1i Podiel tychto cisel: 0.1+0.7i Ak by sme za y zvolili nulu: KomplexneCislo x(1,2), y(0,0), z; dostali by sme výstup: Komplexne cisla: 1+2i, 0+0i Nulou delit neviem!

Spracovanie výnimky Blok try sa v niečom podobá na funkciu – ak sa v bloku vytvoria nejaké lokálne premenné alebo objekty, zaniknú, ak program opustí blok (ak príde na jeho koniec, alebo sa vykoná príkaz throw). Príklad: Delenie komplexných čísel a zánik objektov Do triedy KomplexnéČíslo doplníme deštruktor, ktorý nám oznámi, ktoré komplexné číslo sa ruší. KomplexneCislo::~KomplexneCislo() { cout<<"Rusim komplexne cislo "; Vypis(); cout<<endl; }

Spracovanie výnimky V hlavnom programe vytvoríme a vypíšeme výsledok delenia v bloku try: KomplexneCislo x(1,2), y(3,-1); ... try { KomplexneCislo z; if (y.A()==0 && y.B()==0) throw Delenie_nulou; z.ZmenA((x.A()*y.A()+x.B()*y.B())/(y.A()*y.A()+y.B()*y.B())); z.ZmenB((x.B()*y.A()-x.A()*y.B())/(y.A()*y.A()+y.B()*y.B())); cout<<"Podiel tychto cisel: "; z.Vypis(); } catch(int vynimka) if (vynimka==Delenie_nulou) cout<<"Nulou delit neviem!"<<endl; return 1; cout<<"Program prebehol bez problemov"<<endl

Spracovanie výnimky V tomto prípade prebehne všetko správne. Z výstupu vidíme, že číslo z, v ktorom sa uložil výsledok delenia, zanikne po skončení bloku try, kde aj vzniklo. Ak teraz y bude nulové, vznikne výnimka a blok try sa nedokončí. Vďaka throw sa však aj teraz zavolá deštruktor čísla z.

Spracovanie výnimky Ak by v programe vznikla výnimka nejakého typu a ak by za blokom try nenasledoval vhodný príkaz catch, ktorý by spracovával práve tento typ, program by zavolal systémovú funkciu terminate(). Táto funkcia by po vypísaní štandardnej chybovej hlášky ukončila program. Ukončenie programu pomocou terminate() však nie je korektné. Príklad: Delenie komplexných čísel bez vhodného príkazu catch V predchádzajúcom príklade zmeníme príkaz catch tak, aby zachytával výnimku typu float. Keďže naša výnimka Delenie_nulou je typu int, pre tento typ nebudeme mať k dispozícii príkaz catch. Nový príkaz catch bude vyzerať takto: catch(float vynimka) { if (vynimka==Delenie_nulou) cout<<"Nulou delit neviem!"<<endl; return 1; }

Spracovanie výnimky Dostaneme takýto výstup: Vidíme, že takéto ukončenie nielen nevyzerá dobre, ale nie je ani korektné, keďže sa nezavolal ani jeden deštruktor. Každý príkaz throw musí mať zodpovedajúci príkaz catch!

vykonávanie nasledujúcich Spracovanie výnimky Postup pri zachytávaní a spracovaní výnimiek možno znázorniť takto: vstup do bloku try vykonávanie príkazov bloku nenastala výnimka preskočenie všetkých príkazov catch vykonávanie nasledujúcich príkazov nastala výnimka končí blok try hľadá sa vhodný catch nenájde sa catch zavolá sa terminate() nekorektný koniec nájde sa catch vykonajú sa príkazy v catch pokračuje sa ďalej

Príkaz throw vo funkcii Už sme si hovorili, že príkaz throw môže byť volaný nielen priamo v bloku try, ale aj vo funkcii, ktorá je z tohto bloku volaná. V tomto prípade plní throw v podstate úlohu príkazu return – spôsobí korektný návrat z funkcie, teda návrat so zrušením lokálnych premenných. Príklad: Operátor / pre komplexné čísla Do triedy KomplexnéČíslo doplníme ďalší operátor. V ňom sa bude nachádzať aj príkaz throw. KomplexneCislo KomplexneCislo::operator /(KomplexneCislo x) { KomplexneCislo z; if (x.a==0 && x.b==0) throw Delenie_nulou; z.ZmenA((a*x.a+b*x.b)/(x.a*x.a+x.b*x.b)); z.ZmenB((b*x.a-a*x.b)/(x.a*x.a+x.b*x.b)); return z; }

Príkaz throw vo funkcii V hlavnom programe potom budeme mať v bloku try len samotné delenie: KomplexneCislo x(1,2), y(3,-1), z; cout<<"Komplexne cisla: "; x.Vypis(); cout<<", "; y.Vypis(); cout<<endl; try { z=x/y; } catch(int vynimka) if (vynimka==Delenie_nulou) cout<<"Nulou delit neviem!"<<endl; return 1; cout<<"Program prebehol bez problemov"<<endl; cout<<"Podiel tychto cisel: "; z.Vypis();

Príkaz throw vo funkcii Na výstupe môžeme sledovať, ako vznikajú a zanikajú objekty. Všimnime si aj rušenie kópie potrebnej pri vrátení komplexného čísla funkciou /. Ak delíme nulou, kópia pri vrátení sa nevytvorí, ale vidíme, že lokálna premenná z bola korektne zrušená vďaka throw.

Výnimky viacerých typov Príklad: Vytváranie 3D obrázku z 2D rezov Úlohou programu bude zo zadaného zoznamu 2D obrázkov vytvoriť pospájaním jeden 3D obrázok. Vstupné obrázky budú mať vždy rovnaký názov a líšiť sa budú len v indexe, teda napr. obr1.raw, obr2.raw, obr3.raw atď. Takisto budú mať rovnaké rozmery. Vytvoríme triedu SpracovávačObrázkov, ktorá bude mať metódu Vytvor3Dz2D. Táto metóda oznámi výnimku v prípade, ak nastane problém so vstupným alebo výstupným súborom. V prvom prípade vráti throw meno neplatného vstupného súboru, v druhom len hodnotu typu int. class SpracovavacObrazkov { private: fstream vstup, vystup; public: void Vytvor3Dz2D(char m_vs[100], char m_vy[100], int zaciatok, int koniec, int x, int y); };

Výnimky viacerých typov void SpracovavacObrazkov::Vytvor3Dz2D(char m_vs[100], char m_vy[100], int zaciatok, int koniec, int x, int y) { char meno_vstupu[100]; vystup.open(m_vy,fstream::out | fstream::binary); if (!vystup.is_open()) throw(ChybaVystupu); for (int i=zaciatok; i<=koniec; i++) sprintf(meno_vstupu,"%s%i.raw",m_vs,i); vstup.open(meno_vstupu,fstream::in | fstream::binary); if (!vstup.is_open()) throw(meno_vstupu); for (int j=0;j<x;j++) for (int k=0;k<y;k++) vystup.put(vstup.get()); vstup.close(); } vystup.close(); Konštanta ChybaVýstupu je definovaná na začiatku programu: #define ChybaVystupu 1

Výnimky viacerých typov V hlavnom programe potom máme príkaz catch pre oba typy výnimiek: SpracovavacObrazkov so; try { so.Vytvor3Dz2D("rez","vystup.raw",0,61,128,128); } catch (int vynimka) if (vynimka==ChybaVystupu) cout<<"Do zadaneho suboru neviem zapisovat!"<<endl; return 1; catch (char *vynimka) cout<<"Subor "<<vynimka<<" neexistuje alebo je poskodeny"<<endl; return 2; cout<<"Teraz si mozte 3D obrazok pozriet"<<endl;

Zachytenie všetkých výnimiek Ak chceme mať príkaz catch, ktorý by zachytil výnimku ľubovoľného typu, môžeme to spraviť takto: catch(...) {príkazy} Napr. v predchádzajúcom príklade by sme mohli hlavný program prepísať: SpracovavacObrazkov so; try { so.Vytvor3Dz2D("rez","vystup.raw",0,61,128,128); } catch(...) cout<<"Stalo sa nieco nedobre!"<<endl; return 1; cout<<"Teraz si mozte 3D obrazok pozriet"<<endl;

Opätovné oznámenie výnimky Niekedy môžeme chcieť, aby sa jedna výnimka spracovala na viacerých miestach. Napríklad, ak výnimka nastane vo funkcii, môže na ňu zareagovať samotná funkcia a potom ešte aj hlavný program, ktorý môže napríklad reagovať rovnakým spôsobom na výnimky z viacerých funkcií. Opätovné oznámenie výnimky dosiahneme tým, že v rámci catch zopakujeme použitie throw: catch(typ_výnimky výnimka) { ... throw; } Príklad: Spracovávač obrázkov s opätovným oznámením výnimky Prepíšeme predchádzajúci príklad tak, že chybová hláška sa bude vypisovať už vo funkcii Vytvor3Dz2D. Hlavný program potom zistí, či vo funkcii nastala nejaká výnimka (bez ohľadu na typ) a ak áno, oznámi to užívateľovi.

Opätovné oznámenie výnimky funkcia Vytvor3Dz2D bude teraz vyzerať takto: void SpracovavacObrazkov::Vytvor3Dz2D(char m_vs[100], char m_vy[100], int zaciatok, int koniec, int x, int y) { char meno_vstupu[100]; try vystup.open(m_vy,fstream::out | fstream::binary); if (!vystup.is_open()) throw(ChybaVystupu); } catch (int vynimka) if (vynimka==ChybaVystupu) cout<<"Do zadaneho suboru neviem zapisovat!"<<endl; throw; ...

Opätovné oznámenie výnimky for (int i=zaciatok; i<=koniec; i++) { try sprintf(meno_vstupu,"%s%i.raw",m_vs,i); vstup.open(meno_vstupu,fstream::in | fstream::binary); if (!vstup.is_open()) throw(meno_vstupu); for (int j=0;j<x;j++) for (int k=0;k<y;k++) vystup.put(vstup.get()); vstup.close(); } catch (char *vynimka) cout<<"Subor "<<vynimka<<" neexistuje alebo je poskodeny"<<endl; throw; vystup.close();

Opätovné oznámenie výnimky Hlavný program bude zachytávať výnimky všetkých typov: int main() { SpracovavacObrazkov so; try so.Vytvor3Dz2D("rez","vystup.raw",0,61,128,128); } catch(...) cout<<"3D obrazok si asi nepozrieme :-("<<endl; return 1; cout<<"Teraz si mozte 3D obrazok pozriet"<<endl; return 0;

Alokácia a výnimky Jedna z najčastejších výnimiek vzniká pri neúspešnej alokácii pamäte, najmä ak chceme alokovať viac miesta, ako máme k dispozícii. Príklad: Alokácia bloku pamäte podľa požiadavky užívateľa int velkost; char *blok; cout<<"Kolko bytov pamate mam alokovat? "; cin>>velkost; try { blok=new char[velkost]; if (!blok) throw ChybaAlokacie; //pri neúspechu je hodnota smerníka 0 } catch(int vynimka) if (vynimka==ChybaAlokacie) cout<<"Tak toto nie.."<<endl; return 1; cout<<"V poriadku, alokovane!"<<endl; delete[] blok;

Alokácia a výnimky Otestujeme program v rôznych prípadoch. Ak je alokácia úspešná: Ak sa pokúšame alokovať príliš veľa miesta:

Alokácia a výnimky Ak zadáme nevhodný rozmer, napr. zaporné číslo, vznikne v tomto prípade iný typ výnimky: Ak chceme ošetriť aj takýto prípad, musíme to urobiť osobitne, napr.: try { if (velkost<0) throw ChybaAlokacie; blok=new char[velkost]; if (!blok) throw ChybaAlokacie; }

Alokácia a výnimky V niektorých verziách C++ je operátor new navrhnutý tak, aby oznamoval výnimku, ak je alokácia pamäte neúspešná. V bloku try už preto nemusíme mať príkaz throw, zahŕňa ho samotné new. Výnimka, ktorú new oznamuje, je typu bad_alloc. Príklad: Alokácia bloku pamäte v Linuxe try { blok=new char[velkost]; } catch(bad_alloc vynimka) cout<<"Tak na to zabudnite!"<<endl; return 1;

Alokácia a výnimky Znova otestujeme program pre rôzne prípady. Ak je alokácia v poriadku: Ak chceme alokovať príliš veľa miesta: V tomto prípade sa spracuje a prípad, keď zadáme záporný rozmer:

Výnimky v konštruktoroch Príklad: Výpočet funkčných hodnôt Vytvoríme triedu Graf, ktorá pripraví údaje potrebné na vykreslenie grafu funkcie – pre zadané hodnoty x vypočíta a uloží hodnoty f(x). Trieda bude obsahovať dve dynamicky alokované polia – pre hodnoty x a f(x). class Graf { private: float *x, *fx, a, b; long int n; public: Graf(long int n, float a, float b); ~Graf(); void VypocitajX() {for (int i=0;i<n;i++) x[i]=i*(b-a)/(n-1);} void VypocitajOdmocninu() {for (int i=0;i<n;i++) fx[i]=sqrt(x[i]);} void Vypis(); void ZmenX(int i,float v) {x[i]=v;} };

Výnimky v konštruktoroch Konštruktor triedy Graf oznamuje výnimky pri alokácii: Graf::Graf(long int n, float a, float b) { this->n=n; this->a=a; this->b=b; cout<<"Alokujem pole x.."<<endl; x=new float[n]; if (!x) throw ChybaAlokacie; cout<<"Podarilo sa :-)"<<endl; cout<<"Alokujem pole fx.."<<endl; fx=new float[n]; if (!fx) throw ChybaAlokacie; cout<<"Aj to sa podarilo :-)"<<endl; }

Výnimky v konštruktoroch Deštruktor a metóda Výpis: Graf::~Graf() { cout<<"Dealokujem x.."<<endl; delete[] x; cout<<"Dealokujem fx.."<<endl; delete[] fx; } void Graf::Vypis() cout<<"Vypis funkcnych hodnot:"<<endl; for (int i=0;i<n;i++) cout<<"x: "<<x[i]<<", fx: "<<fx[i]<<endl;

Výnimky v konštruktoroch float a,b; long int n; cout<<"Zaciatok intervalu: "; cin>>a; cout<<"Koniec intervalu: "; cin>>b; cout<<"Kolko hodnot sa ma vypocitat? "; cin>>n; try { Graf g(n,a,b); g.VypocitajX(); g.VypocitajOdmocninu(); g.Vypis(); } catch(int vynimka) { if (vynimka==ChybaAlokacie) cout<<"Tuto alokaciu asi nezvladnem :-("; return 1; cout<<"Program prebehol bez problemov!"<<endl;

Výnimky v konštruktoroch Ak všetko prebehne v poriadku, môže nám dať program napríklad takýto výstup:

Výnimky v konštruktoroch V prípade, že zadáme príliš veľký rozmer, nastane výnimka: Všimnime si nasledujúci prípad. V tomto prípade sme zadali taký rozmer, že sa síce podarilo alokovať miesto pre x, ale pre fx sa to už nepodarilo. Konštruktor sa teda nedokončí, vytvorenie objektu triedy Graf neprebehne. To znamená, že sa nezavolá ani deštruktor (vidíme to aj z výstupu). Miesto pre x sa teda nemá kde uvoľniť, pritom je už alokované. Vznikol memory leak. Takýmto situáciám by sme mali predchádzať.

Výnimky v konštruktoroch V našom prípade môžeme situáciu vyriešiť napr. tak, že príslušný catch bude už v konštruktore: Graf::Graf(long int n, float a, float b) { this->n=n; this->a=a; this->b=b; try cout<<"Alokujem pole x.."<<endl; x=new float[n]; if (!x) throw ChybaAlokacie; } catch(int vynimka) if (vynimka==ChybaAlokacie) cout<<"Pole x neviem alokovat :-("<<endl; throw;

Výnimky v konštruktoroch V ďalšej časti budeme mať catch pre alokáciu fx, pričom ak nastane výnimka, dealokuje sa aj miesto alokované pre x: cout<<"Podarilo sa :-)"<<endl; try { cout<<"Alokujem pole fx.."<<endl; fx=new float[n]; if (!fx) throw ChybaAlokacie; } catch(int vynimka) if (vynimka==ChybaAlokacie) cout<<"Pole fx je uz na mna privela :-("<<endl; cout<<"Dealokujem aj x"<<endl; delete[] x; throw; cout<<"Aj to sa podarilo :-)"<<endl;

Výnimky v konštruktoroch Na výstupe vidíme, že ak nastane situácia, že x sa podarí alokovať a fx nie, program sa korektne ukončí:

Zhrnutie Výnimka je problém, ktorý nastane počas behu programu a zabráni jeho správnemu priebehu. Výnimky sú spôsobené vonkajšími okolnosťami, nie programátorom. Programátor ich ale môže predvídať a dopredu ošetriť výnimočné situácie. Na zachytenie a spracovanie výnimiek máme v C++ trojicu príkazov try, throw a catch. V bloku try je obsiahnutá tá časť kódu, v ktorej môže nastať výnimka. Príkaz throw musí byť buď v bloku try alebo vo funkcii, ktorá je z tohto bloku volaná. Každý typ výnimky musí mať zodpovedajúci príkaz catch.