Sunteți pe pagina 1din 12

8

Tratarea excepiilor

Tratarea excepiilor permite rezolvarea n mod unitar a erorilor de execuie. Mecanismul de tratare a excepiilor din C++ ofer posibilitatea ca o problem care apare ntr-o funcie i aceasta nu o poate rezolva, s fie definit ca o excepie, cu sperana c funcia apelant va trata aceast problem. O funcie care dorete s trateze astfel de probleme va indica acest lucru prin captarea excepiei respective, iar codul care se execut la captare se numete rutin de tratare a excepiei (exception handler). Ca exemplu, se consider modul n care poate fi tratat o depire de dimensiune n clasa Vector.
class Vector{ int* p; int sz; public: class Range {}; // exceptie la depasire dimens. int& operator[](int i); // ... }; int& Vector::operator[](int i){ if (i < 0 || i > sz) throw Range(); return p[i]; } void f(Vector &v){ try { fv(v); // apelul unei funcii } catch(Vector::Range){ // Rutina de tratare a exceptiei // Vector::Range // In acest punct se ajunge numai dac // in functia fv(v) s-a apelat operatorul [] // cu un indice n afara domeniului } }

202

Elemente de Programare Orientat pe Obiecte

Construcia:
catch ( /*.. */){ // . }

este numit rutin de tratare a excepiei (exception handler). Ea este apelat imediat dup un bloc prefixat de cuvntul cheie try sau dup o alt rutin de tratare a unei excepii. Paranteza care urmeaz cuvntului cheie catch conine o declaraie care este folosit ca un argument: aceast declaraie specific tipul excepiei de care se ocup rutina de tratare a erorii i, uneori, numele argumentului. Dac o excepie (ceea ce nsemn o eroare) apare ntr-un bloc prefixat de specificatorul try, ea este lansat de ctre instruciunea throw, dup care este captat ntr-o rutin de tratare a excepiilor (deci n blocul instruciunii catch) i prelucrat. Blocul try trebuie s conin acea seciune a programului n care se dorete s fie cutate erorile. El poate cuprinde cteva instruciuni ntr-o funcie sau chiar ntregul corp al funciei main(), ceea ce nseamn urmrirea erorilor n ntregul program. Atunci cnd o funcie genereaz o excepie, ea nu i mai continu execuia ca n situaia n care ar apela o funcie de tratare a erorii, din care se revine printr-o instruciune return, ci execuia este captat n rutina de tratare, dup care se continu programul cu instruciunile care urmeaz rutinei de tratare a excepiei.

8.1

Discriminarea excepiilor

n mod obinuit, ntr-un program pot aprea mai multe tipuri de erori de execuie (run-time errors) i fiecare tip de eroare poate fi asociat unei excepii cu un nume distinct. Captarea excepiilor este executat de mai multe blocuri catch, care pot fi asociate unuia sau mai multor blocuri try. Forma general pentru un bloc try urmat de mai multe blocuri catch care trateaz diferite tipuri de excepii este urmtoarea:
try { // bloc try
}

catch (tip_arg1 arg1) { // bloc de tratare a excepiei // de tipul tip_arg1


}

catch (tip_arg2 arg2) { // bloc catch de tratare a excepiei // de tipul tip_arg2


} .

catch (tip_argn argn) { // bloc de tratare a excepiei // de tipul tip_argn }

8. Tratarea Excepiilor

203

O astfel de construcie este asemntoare unei instruciuni switch, n care selecia unei rutine (un bloc catch) se face n funcie de tipul excepiei lansate n blocul try, fr s fie necesar o instruciune break. Dac n blocul try nu apare nici o excepie, toate blocurile catch consecutive acestuia sunt ignorate. La apariia unei excepii, controlul programului este transferat rutinei de tratare a excepiei corespunztoare tipului acesteia, toate celelate blocuri catch fiind ignorate. Dup execuia unei rutine de tratare a excepiei, programul fie continu cu execuia instruciunilor urmtoare blocurilor catch, fie se termin, dac rutina respectiv a apelat o funcie exit() sau abort(). Dac se lanseaz o excepie pentru care nu exist nici o rutin de tratare (bloc catch), atunci este posibil s apar o execuie anormal a programului. Exemplul 8.1
void fd(){ char tip; while (tip !='q'){ try{ int argi; double argd; char argc; cout << Start bloc try\n; cout << "Introduceti tipul si arg. exceptiei: "; cin >> tip; switch (tip){ case 'i': cin >> argi; cout << "Lansare exceptie int\n"; throw argi; break; case 'd': cin >> argd; cout<<"Lansare except. double\n"; throw argd; break; case 'c': cin >> argc; cout<<"Lansare exceptie char\n"; cout.flush(); throw argc; break; } cout << "Nu s-a lansat exceptie\n"; } catch(int i){ cout<<"Captare exceptie int i="<<i<<endl; } catch(double d){ cout<<"Captare exceptie double d="<<d<<endl; }

204

Elemente de Programare Orientat pe Obiecte cout << "End bloc try-catch\n";

n aceast funcie se poate selecta tipul i argumentul unei excepii prin valori introduse de la consol. La selecia tipului de argument int (prin introducerea de la tastatur a caracterului i), se lanseaz o excepie pentru tipul de date integer, cu o valoare a argumentului egal cu valoarea citit de la consol. Aceast excepie este captat de instruciunea catch(int i), care primete n argumentul i valoarea cu care a fost lansat excepia de tip integer i o afieaz. Cu acest program se pot testa diferitele situaii care pot aprea n execuia blocurilor try-catch: selectarea uneia din rutinele de tratare a excepiilor (unul din blocurile catch) n funcie de tipul excepiei lansate n blocul try, ignorarea tuturor blocurilor catch dac n blocul try nu se lanseaz nici o excepie, precum i terminarea anormal a programului n situaia n care s-a lansat o excepie, dar nu exist o rutin de tratare pentru tipul excepiei lansate. Liniile de afiare care se pot obine la execuia funciei fd(), pentru diferite situaii selectate, sunt prezentate mai jos:
Start bloc try Introduceti tipul si argumentul excepiei: i 2 Lansare exceptie int Captare exceptie int i=2 End bloc try-catch Start bloc try Introduceti tipul si argumentul excepiei: d 3.5 Lansare exceptie double Captare exceptie double d=3.5 End bloc try-catch Start bloc try Introduceti tipul si argumentul excepiei: g Nu s-a lansat exceptie End bloc try-catch Start bloc try Introduceti tipul si argumentul excepiei: c a Lansare exceptie char Abnormal program termination

n mod asemntor, n clasa Vector pot fi tratate mai multe tipuri de erori: n afar de eroarea de depire a dimensiunii vectorului, poate fi tratat i eroarea care apare datorit unei valori inacceptabile la construcia vectorului:
class Vector{ int* p; int sz; enum {max=1000};

8. Tratarea Excepiilor public: class Range {}; // exceptie la depasire domeniu class Size {}; // exceptie la constructie Vector(int s); int& operator[](int i); // .... }; Vector::Vector(int s){ if (s < 0 || max < s) throw Size(); sz = s; p = new int[s]; }

205

Aa cum s-a artat mai sus, operatorul de indexare [] al clasei Vector va lansa excepia de tip (clas) Range dac este apelat cu un indice n afara domeniului. n mod similar, constructorul Vector::Vector va lansa o excepie de tip Size, dac este apelat cu un argument cu o valoare inacceptabil. n utilizarea clasei Vector se poate discrimina ntre cele dou excepii prin adugarea a dou rutine de tratare a excepiilor dup un bloc try:
void fex(Vector &v){ try { fv(v); // apelul unei funcii cu arg. v } catch(Vector::Range){ // Rutina de tratare a exceptiei // Vector::Range // In acest punct se ajunge numai dac // in functia fv(v) s-a apelat operatorul [] // cu un indice n afara domeniului } catch(Vector::Size){ // Rutina de tratare a exceptiei // Vector::Size // In acest punct se ajunge daca in functia // fv(v) s-a apelat un constructor // cu un argument cu o valoare inacceptabila } }

8.2

Modaliti de tratare a excepiilor

n C++ exist mai multe modaliti de tratare a excepiilor, dintre care o parte vor fi descrise n continuare.

8.2.1 Tratarea tuturor excepiilor


n Exemplul 8.1 a fost prezentat o situaie n care nu era tratat o excepie de un anumit tip, situaie care provoac execuia anormal a programului, atunci cnd o

206

Elemente de Programare Orientat pe Obiecte

astfel de excepie este lansat n blocul try corespunztor. Astfel de erori pot fi evitate dac se folosete o construcie de captare a tuturor erorilor, care are urmtoarea sintax:
catch(){ // tratare general excepii }

Instruciunea catch() indic captarea oricror excepii (corespunztoare tuturor tipurilor de date). Tratarea excepiilor n astfel de situaii poate fi o tratare general. Mai este posibil combinarea uneia sau mai multor instruciuni catch de captare a unor tipuri specifice de excepii cu o instruciune de captare a tuturor excepiilor, combinaie prin care se urmrete tratarea difereniat a unor anumite tipuri de excepii, toate celelalte fiind tratate global. Astfel de construcii permit evitarea terminrii anormale a programului. Dac n funcia fd() din Exemplul 8.1 se adaug dup blocul try o instruciune catch() de captare general, atunci nu mai apare eroarea de execuie la lansarea unei excepii netratate specific:
//.. try{ // acelasi bloc ca in Exemplul 8.1 } catch(int i){ cout << "Captare exceptie int i = " << i <<endl; } catch(double d){ cout << "Captare exceptie double d = " << d <<endl; } catch(){ cout << "Captare exceptie oarecare\n; } //.

La lansarea excepiei cu argument de tip caracter (throw argc) se transfer controlul rutinei de captare general a excepiilor (care, n exemplul de mai sus afieaz doar un mesaj la consol) i nu mai apare execuia anormal a programului.

8.2.2 Transferul de informaii ctre rutina de tratare a excepiei


O excepie poate fi captat prin specificarea tipului ei. Totui, ceea ce se lanseaz (throw) n momentul apariiei unei erori nu este un tip de date ci un obiect de tipul respectiv, care este construit la apariia erorii. De aceea, n definirea unui tip excepie (clas) se pot aduga date membre care pot fi setate la construcia obiectului lansat i utilizate n rutina de tratare a excepiilor pentru identificarea condiiilor de apariie a erorii.

8. Tratarea Excepiilor

207

De exemplu, clasa Range de definire a excepiei de depire a dimensiunii vectorului poate s conin o dat membr index care memoreaz valoarea indicelui care a produs depirea i o funcie public getindex() care permite citirea acestei valori, astfel nct valoarea indicelui care a produs depirea poate fi aflat i, eventual, afiat n rutina de tratare a erorii. La fel se poate proceda i pentru clasa Size. Exemplul urmtor completeaz clasa Vector i clasele excepie definite de aceasta i ilustreaz crearea i utilizarea obiectelor de clas excepie. Exemplul 8.2

n clasa Vector sunt definite clasele excepie Range i Size care permit stocarea i regsirea unor informaii prin intermediul obiectelor lansate la apariia unei erori de un anumit tip. La apelul unei funcii operator de indexare [] a clasei Vector cu o valoare a indicelui i < 0 sau i >= sz, se lanseaz excepia de depire a domeniului prin construirea unui obiect din clasa Range, cu argumentul i: Range(i). Obiectul lansat memoreaz (prin construcie) aceast valoare. Rutina de tratare a excepiei de depire a domeniului are ca argument un obiect r de clas Vector::Range, care este chiar obiectul lansat la apariia erorii n funcia operator de indexare. Folosind funciile membre ale acestui obiect, rutina de tratare a excepiei poate regsi informaii privind condiia de apariie a erorii.
class Vector{ int* p; int sz; enum {max=1000}; public: Vector(int s); class Range { // clasa exceptie int index; public: Range(int i){index = i;} int getindex(){return index;} }; class Size { // clasa exceptie int dim; public: Size(int d){dim=d;} int getdim() {return dim;} }; int& operator[](int i); int getsize() {return sz;} // .... }; Vector::Vector(int s){ if (s < 0 || max < s) throw Size(s); sz = s; p = new int[s]; }

208

Elemente de Programare Orientat pe Obiecte int& Vector::operator[](int i){ if (i < 0 || i > sz) throw Range(i); return p[i]; } void fv(Vector& v, int d){ Vector v2(d); for (int i=0;i<d;i++){ v2[i] = v[i] = i; } } void fex(Vector &v, int d){ try{ fv(v, d); } catch(Vector::Range r){ cerr << "Indicele " << r.getindex() << " in afara domeniului\n"; return; } catch(Vector::Size s){ cerr << "Dimensiunea " << s.getdim() << " depaseste valoarea admisa\n"; return; } cout << "Vectori OK" << endl; return 0; } void main(){ Vector v1(100); int d; cout << "Introduceti dimensiunea: "; cin >> d; while(fex(v1,d)){ cout << "Introduceti dimensiunea: cin >> d; } ";

La execuia acestui program se creeaz mai nti un vector de dimensiune 100, dup care se citete de la tastatur dimensiunea unui alt vector. n funcie de valoarea introdus la consol, pot aprea trei situaii distincte. Dac dimensiunea introdus d este mai mic sau egal cu dimensiunea primului vector (100), atunci nu apare nici o eroare de execuie, programul afieaz un mesaj la consol dup care se oprete. Dac dimensiunea introdus d este mai mare dect dimensiunea primului vector (100), dar mai mic dect valoarea maxim admisibil pentru construcia vectorilor (max=1000), atunci apare o eroare de depire a domeniului n funcia operator de indexare [] i se lanseaz excepia prin construirea unui obiect de clas

8. Tratarea Excepiilor

209

Range, cu argument al constructorului egal cu valoarea primului indice care depete domeniul. Aceast valoare este regsit n funcia de tratare a excepiei, care o afieaz la consol. Mesajele care apar la consol n aceast situaie sunt:
Introduceti dimensiunea: 200 Indice 100 in afara domeniului Introduceti valoarea:

Dac dimensiunea introdus d este mai mare dect valoarea maxim admisibil (1000), atunci apare o eroare n momentul construciei obiectului v2 de tip Vector n funcia fex(). n constructorul Vector::Vector, la apariia unei astfel de erori se lanseaz o excepie prin construirea unui obiect de tip Size, cu valoare a argumentului d, valoare care este memorat n data membr privat dim a clasei Size. Aceast valoare este regsit n funcia de tratare a excepiei, care o afieaz la consol. Mesajele care apar la consol n aceast situaie sunt:
Introduceti dimensiunea: 1001 Dimensiunea 1001 depaseste valoarea admis Introduceti dimensiunea:

8.2.3

Imbricarea rutinelor de tratare a excepiilor

Limbajul C++ admite construcii imbricate de tratare a execpiilor, ca de exemplu:


try{ // bloc try } catch (ExceptionX){ try { // o rutin care incearc // tratarea excepiei } catch(ExceptionX){ // o alta rutin de tratare // care este lansat dac prima rutin // a provocat o nou excepie } }

O excepie este tratat de rutina imediat urmtoare blocului try, dar i n aceast tratare poate fi lansat o nou excepie (eventual de acelai tip), de care se va ocupa un nou bloc catch, imbricat n primul bloc try. Dei posibile, astfel de construcii imbricate sunt rar utilizate sau utile.

210

Elemente de Programare Orientat pe Obiecte

8.2.4 Gruparea excepiilor


De cele mai multe ori, excepiile se pot grupa n familii, n funcie de categoria la care se refer. De exemplu, erori de depire superioar, depire inferioar de mprire la zero i altele pot fi grupate n categoria de erori n operaii aritmetice cu numere flotante; erori de deschidere fiier, nchidere fiier, erori de citire sau erori de scrire, pot fi grupate ca erori n operaii cu fiiere, .a.md. n astfel de situaii, rutinele de tratare a excepiilor sunt destul de asemntoare i se pot implementa ntr-un mod unitar. Implementarea unitar a grupurilor de excepii se poate face n dou modaliti: prin enumerare i prin organizarea excepiilor n ierarhii de clase folosind motenirea i funciile virtuale. n abordarea prin enumerare a grupurilor de excepii se definete un tip enumerare ale crui posibile valori sunt numele tuturor excepiilor din grup, iar selecia ntre rutinele specifice se face printr-o instruciune switch:
class Overflow { }; class Underflow { }; class Dividebyzero { }; enum Mathenum {Overflow, Underflow, Dividebyzero}; void fenum(){ try{ // lansare excepii } catch (Mathenum m){ switch (m){ case Overflow: // tratare depasire superioara break; case Underflow: // tratare depasire inferiora break; case Dividebyzero: // tratare impartire la 0 break; } } }

O astfel de organizare a grupurilor de excepii, dei posibil, este voluminoas i cu mari riscuri de a fi omise unele situaii, care, bineneles, vor provoca terminarea anormal a programului. n plus, la adugarea unei noi excepii ntr-un grup de excepii ale unei biblioteci, ar fi necasar recompilarea tuturor modulelor care conin rutine de captare a excepiilor. Acest lucru este, bineneles, imposibil i de aceea ar fi preferabil s nu se adauge noi excepii ntr-o bibliotec dac tratarea grupurilor de excepii este implementat prin enumerarea acestora. O soluie mai elegant i mai robust o reprezint organizarea excepiilor n ierarhii de clase. De exemplu:

8. Tratarea Excepiilor class Matherr { }; class Overflow : public Matherr{ }; class Underflow : public Matherr { }; class Dividebyzero : public Matherr { }; void fder(){ try{ // lansare excepii } catch (Overflow){ // tratare depasire superioara } catch (Matherr){ // tratare orice exceptie Matherr // care nu este Overflow } }

211

n acest exemplu, dintre toate excepiile care pot fi lansate n blocul try, excepia Overflow are o tratare special, n timp ce toate celelalte excepii derivate din clasa Matherr au o tratare global prin rutina catch(Matherr), deci nu vor provoca terminarea anormal a programului. Din exemplele prezentate pn acum se poate observa c excepiile pot fi definite global, pentru ntregul program, cum sunt excepiile Matherr sau Overflow, sau pot fi excepii locale ale unei clase, cum sunt excepiile Range sau Size, definite n clasa Vector. Pentru astfel de excepii, n instruciunea catch trebuie s fie specificat domeniul clasei excepie: catch (Vector::Range).

Exerciii
E8.1 S se implementeze o funcie de copiere de la un fiier surs ntr-un fiier destinaie, n care s se lanseze excepii pentru urmtoarele situaii: erori de deschidere sau nchidere fiiere, erori de citire din fiierul surs, erori de scriere n fiierul destinaie. Pentru fiecare tip de eroare s se defineasc o clas excepie, iar apelul funciei de copiere s fie executat ntr-un bloc try, urmat de rutinele de tratare a excepiilor corespunztoare. E8.2 S se defineasc o clas Int care s se comporte exact ca tipul fundamental int, cu deosebirea c lanseaz excepii n situaiile de depire inferioar i superioar care apar n diferite operaii aritmetice. E8.3 Se consider clasa IntArray, vector de numere ntregi, descris n E2.6 i E4.10. S se modifice funcia membr GetAt() i funcia operator de indexare, astfel nct s lanseze o excepie atunci cnd sunt apelate cu un indice n afara domeniului vectorului de numere. Se va defini o clas excepie Range local clasei IntArray() i o rutin de tratare a execepiei.

212

Elemente de Programare Orientat pe Obiecte

E8.4 Pentru clasele IntSList i IntDList (descrise n seciunea 3.1.4) s se defineasc excepia de extragere, prin intermediul unei clase definite global, astfel nct s poat fi utilizat att pentru clasa IntSList ct i pentru clasa IntDList. Excepia de extragere este lansat n funciile RemoveHead() sau RemoveTail(), atunci cnd numrul de elemente ale listei (count) este 0. ntr-o funcie care utilizeaz liste nlnuite, s se introduc blocurile de captare i tratare a acestor excepii. E8.5 Pentru clasa IntTree (descris n seciunea 3.2.3) s se defineasc dou tipuri de excepii, excepia de alocare i excepia de extragere, prin intermediul a dou clase definite local. Excepia de alocare este lansat n funcia insert(), atunci cnd operatorul new de alocare dinamic returneaz pointer nul. Eroarea de extragere este lansat n funciile remove(), atunci cnd nu este gsit data care trebuie s fie extras din arbore. ntr-o funcie care utilizeaz un arbore binar ordonat, s se introduc blocurile de captare i tratare a acestor excepii. E8.6 S se defineasc o clas template TVector cu clase locale pentru excepiile Range i Size. E8.7 Pentru clasa template TBTree (definit n seciunea 7) care implementeaz o mulime printr-un un arbore binar ordonat, s se defineasc o clas local pentru definirea excepiei la cutarea unui element n mulime: dac elementul nu este gsit, funcia de cutare va lansa o excepie. Se va particulariza pentru un arbore binar sortat de iruri de caractere (obiecte de clas String). E8.8 S se implementeze o funcie de nsumare a elementelor unui obiect de tip Vector (clasa Vector a fost descris n seciunea 8.2) care s lanseze o excepie dac se depeste capacitatea de reprezentare a numerelor ntregi. E8.9 Pentru clasa template TArray (definit n E7.14) care implementeaz un vector de obiecte de diferite tipuri, s se modifice funcia InsertAt() i funcia operator de indexare astfel nct s lanseze o excepie, definit printr-o clas local TRange, atunci cnd indexul de apel este n afara domeniului vectorului. S se implementeze rutina de tratare a acestei excepii. E8.10 S se suprancarce operatorul de indexare al clasei Date (definit n E2.1 i E4.1) astfel nct la indicele 0 s corespund variabila zi, la indicele 1, s corespund luna, iar la indicele 2 sa corespund anul. S se atribuie variabilei lun din obiectul d2, valoarea variabilei corespunztoare din obiectul d1 i s se afieze rezultatul la consol. Pentru valori ale indicelui n afara domeniului 0-2, funcia operator[]() va lansa o excepie definit printr-o clas declarat n interiorul clasei Date. ntr-o funcie oarecare f() s se execute o instruciune care provoac depire de indice, s se capteze aceast excepie i s se trateze ntr-o rutin de tratare a excepiilor. Aceast rutin trebuie s afieze mesajul: Indicele cu valoarea: depeste domeniul, unde . reprezint valoarea indicelui care a provocat excepia.

S-ar putea să vă placă și