Sunteți pe pagina 1din 7

12. Template-uri de funcii.

Tratarea excepiilor
Template-urile (abloanele) reprezint una dintre cele mai puternice caracteristici ale limbajului C++. Acestea permit definirea, printr-un singur segment de cod, a unei ntregi game de funcii suprancrcate template de funcie sau a unei ntregi serii de clase template de clas. n acest capitol vom discuta cazul funciilor. Putem scrie generic, de exemplu, un singur template de funcie pentru ordonarea unui ir de elemente, iar funcia poate fi apelat pentru valori de diverse tipuri: int, double etc. Tratarea excepiilor este o metod care permite programatorilor s scrie programe mai robuste, mai clare i cu un grad mai mare toleran la erori. Programele pot fi concepute astfel nct s detecteze i s trateze erorile (excepiile) care pot aprea n timpul rulrii, evitnd astfel ntreruperile neplanificate ale aplicaiilor.

12.1

Template-uri de funcii

Funciile suprancrcate realizeaz, de obicei, operaii similare pentru diferite tipuri de date. Dac operaiile sunt identice pentru toate tipurile de date, acestea pot fi realizate mai compact folosind template-uri de funcii. Programatorul trebuie s scrie doar o singur definiie de template de funcie. Bazndu-se pe tipurile argumentelor date explicit sau rezultate din apeluri ale acestor funcii, compilatorul genereaz funcii separate n codul obiect pentru a manipula corespunztor fiecare tip de apel. n limbajul C, acest lucru poate fi realizat folosind macrouri create prin directiva de preprocesare #define. Este, ns, o metod care poate produce efecte nedorite i care nu permite compilatorului s fac verificri de tip. Toate definiiile de template-urile de funcii ncep prin cuvntul cheie template urmat de lista parametrilor formali de tip ai template-ului de funcie plasat ntre < >. Fiecare parametru formal de tip trebuie s fie precedat de cuvntul cheie class sau de typename. Parametrii formali de tip din definiia unui template de funcie sunt utilizai pentru a specifica tipurile argumentelor funciei, tipul valorii returnate de funcie i pentru a declara variabile n funcie. Definiia funciei este, astfel, cea a unei funcii obinuite. Cuvintele cheie class sau typename folosite pentru a specifica parametrii de tip ai template-ului au semnificaia de orice tip de dat predefinit sau definit prin program. Programul de mai jos definete funcia PrintArray care tiprete componentele unor tablouri. #include <iostream>

Programarea calculatoarelor i limbaje de programare I

<iostream> using std::cout; using std::endl; template< class T > void PrintArray(const T *array, const int count) { for(int i = 0; i < count; i++) cout << array[i] << " "; cout << endl; } int main() { const int aCount = 5, bCount = 7, cCount = 6; int a[aCount] = {1, 2, 3, 4, 5}; double b[bCount] = {1.1, 2.2, 3.3, 4.4, 5.5, 6.6, 7.7}; char c[cCount] = "HELLO"; cout << "Tabloul a contine: " << endl; PrintArray(a, aCount); cout << "Tabloul b contine: " << endl; PrintArray(b, bCount); cout << "Tabloul c contine: " << endl; PrintArray(c, cCount); return 0; } Acest program afieaz: Tabloul a contine: 1 2 3 4 5 Tabloul b contine: 1.1 2.2 3.3 4.4 5.5 6.6 7.7 Tabloul c contine: H E L L O Funcia template PrintArray declar un singur parametru formal de tip cu numele T. Identificatorul T poate fi nlocuit cu orice alt identificator valid. Prin T se definete generic tipul tabloului ale crui componente urmeaz s fie tiprite. Atunci cnd compilatorul detecteaz n program un apel al funciei PrintArray, tipul argumentului generic este nlocuit cu tipul parametrului actual i se creeaz o funcie pentru tiprirea tabloului cu acest tip. Noua funcie este apoi compilat. n exemplul de mai sus, sunt instaniate trei funcii PrintArray, cu argumente de tip int, double i char. Exemplu Instanierea pentru tipul de dat int este: void PrintArray(const int *array, const int count) { for(int i = 0; i < count; i++)

Programarea calculatoarelor i limbaje de programare I

cout << array[i] << " "; cout << endl; } Fiecare parametru formal de tip din definiia unui template de funcie apare, n mod normal, cel puin o dat n lista de parametri ai funciei cel puin o dat. Numele unui parametru formal de tip poate fi folosit o singur dat n lista de parametri ai header-ului unui template. n programul de mai sus, funcia PrintArray este apelat de trei ori cu argumentele a de tip int*, b de tip double* i c de tip char* pentru primul parametru. Apelul PrintArray(a, aCount); face ca apariiile lui T n funcia PrintArray s fie transformate n int i compilatorul instaniaz funcia PrintArray pentru T de tip int. n mod asemntor se ntmpl i pentru apelurile PrintArray(b, bCount); PrintArray(c, cCount); n acest program, mecanismul template-urilor face ca programatorul s nu mai fie nevoit s scrie trei funcii suprancrcate cu prototipurile void PrintArray(const int *, const int); void PrintArray(const double *, const int); void PrintArray(const char *, const int);

12.2

Tratarea excepiilor

Exist mai multe tehnici prin care pot fi tratate erorile care apar n timpul rulrii unui program. Cea mai folosit este aceea de a introduce n program secvene de cod adaptate prevenirii unor posibile situaii nedorite. Erorile sunt tratate n locul n care apar n program. Avantajul acestei abordri este c persoana care citete codul poate vedea modalitatea de prelucrare a erorii n vecintatea codului i poate determina dac metoda de tratare a excepiei este implementat corect. Dezavantajul este c prin aceast schem codul este oarecum poluat cu secvene de procesare a erorilor i devine mult mai greu de citit de un programator care este concentrat pe aplicaia nsi. Aceast metod face codul mult mai greu de neles i de meninut. Tratarea excepiilor n C++ permite programatorilor s nlture partea de tratare a excepiilor din secvena principal de program. Stilul C++ de tratare a excepiilor permite interceptarea tuturor excepiilor sau a tuturor excepiilor de un anumit tip. Aceasta face programul mult mai robust, reducnd probabilitatea de ntrerupere neplanificat a programului. Tratarea excepiilor se folosete n situaia n care programul poate s i continue rularea dup ce depete eroarea care provoac excepia.

Tratarea excepiilor folosind try, throw i catch


Tratarea excepiilor n C++ este o metod care se aplic atunci cnd funcia care detecteaz o eroare nu o i trateaz. Ea doar genereaz sau arunc excepia (throw). Nu se garanteaz c excepia va fi i tratat n afara funciei. Pentru aceasta, trebuie specificat o secven de cod care detecteaz sau prinde excepia i o trateaz. Programatorul trebuie s includ ntr-un bloc try codul care ar putea genera o eroare generatoare a unei excepii. Blocul try este urmat de unul sau mai multe

Programarea calculatoarelor i limbaje de programare I

blocuri catch. Fiecare bloc catch specific tipul excepiei pe care o poate detecta i trata. Dac excepia se potrivete cu tipul parametrului unuia dintre blocurile catch, se execut codul acelui catch. Dac nu este identificat niciun bloc catch care s trateze eroarea, se apeleaz funcia predefinit terminate care la rndul ei apeleaz n mod implicit funcia predefinit abort pentru ntreruperea programului. Dac ntr-un bloc try nu se genereaz nicio excepie, programul ruleaz ignornd blocurile catch asociate lui. Exemplu #include <iostream> #include <stdexcept> using std::cin; using std::cout; using std::endl; using std::runtime_error; double Fractie(int numarator, int numitor) { if(numitor == 0) throw runtime_error("Numitorul este 0"); return static_cast<double>(numarator)/numitor; } int main() { int numar1, numar2; double rezultat; cout << "Introduceti doi intregi (EOF pentru a termina): "; while(cin >> numar1 >> numar2) { try { rezultat = Fractie(numar1, numar2); cout << "Valoarea fractiei este: " << rezultat << endl; } catch(runtime_error e) { cout << "A aparut urmatoarea exceptie: " << e.what() << endl; } cout << "Introduceti doi intregi (EOF pentru a termina): "; } cout << endl; return 0; } Un exemplu de rulare a acestui program este urmtorul: Introduceti doi intregi (EOF pentru a termina): 3 4 Valoarea fractiei este: 0.75 Introduceti doi intregi (EOF pentru a termina): 5 0

Programarea calculatoarelor i limbaje de programare I

A aparut urmatoarea exceptie: Numitorul este 0 Introduceti doi intregi (EOF pentru a termina): CTRL-D Programul de mai sus calculeaz rezultatul mpririi a dou numere ntregi. Dac numitorul este 0, este declanat o excepie i se semnaleaz eroarea. Programul conine un bloc try care cuprinde codul care ar putea genera o excepie. Operaia de mprire a celor dou numere este implementat prin funcia Fractie care genereaz o excepie prin instruciunea throw atunci cnd numitorul este 0. Clasa runtime_error face parte din biblioteca standard i este declarat n fiierul header stdexcept. Este derivat din clasa de baz exception care face parte tot din biblioteca standard, fiind declarat n fiierul header exception i care este folosit pentru a declana excepii n C++. Declanarea excepiilor cu ajutorul lui runtime_error permite adugarea unui mesaj care poate fi citit prin metoda what(). Limbajul C++ ofer o ntreag gam de clase derivate din exception, destinate diverselor tipuri de excepii care se pot declana ntr-un program. La rndul su, programatorul poate s i defineasc propriile sale clase pentru declanarea excepiilor, derivndu-le, spre exemplu, din exception sau din clasele derivate din ea. n exemplul de mai sus, blocul try este urmat imediat de un catch care conine codul de tratare a excepiei de tip runtime_error. Atunci cnd ntr-un bloc try este generat o excepie, ea va fi tratat de blocul catch care este destinat acelui tip particular de excepie. Aadar, blocul catch din exemplu va trata doar excepiile de tip runtime_error. Dac n timpul execuiei codul din blocul try nu genereaz nicio excepie, toate blocurile catch asociate acestui try vor fi ignorate, iar execuia cotinu cu prima instruciune care urmeaz dup acestea.

Generarea unei excepii throw


Cuvntul cheie throw se folosete pentru a indica faptul c a aprut o excepie. Un throw specific, n mod obinuit, un operand. Operandul lui throw poate fi de orice tip. Dac este un obiect, l vom numi obiect excepie. Putem folosi, ns, i obiecte care nu sunt destinate tratrii excepiilor, i chiar i expresii de orice tip. Imediat ce a fost declanat o excepie, aceasta va fi detectat de cel mai apropiat secven de cod destinat tratrii excepiei respective. Secvenele de tratare a excepiilor generate ntr-un bloc try sunt listate imediat dup blocul try. Ca regul general pe care este bine s o urmm atunci cnd scriem programe, excepiile trebuie declanate doar n interiorul blocurilor try. O excepie care apare n afara unui try poate conduce la terminarea programului.

Tratarea unei excepii catch


Tratarea excepiilor se face prin blocuri catch. Fiecare astfel de bloc const din cuvntul cheie catch urmat, ntre paranteze rotunde, de tipul excepiei i, opional, de un nume de parametru. ntre acolade se gsete codul care trateaz excepia. Atunci cnd este detectat o excepie, se execut codul dintre acolade. Blocul catch definete un domeniu de accesibilitate propriu. Dac parametrul are nume, atunci el poate fi invocat n blocul catch. Dac nu are nume, el este 5

Programarea calculatoarelor i limbaje de programare I

specificat numai n scopul potrivirii dintre blocul catch i tipul excepiei creia i este destinat. O excepie care nu este detectat apeleaz n mod implicit funcia terminate() din biblioteca standard care, la rndul ei, termin programul prin apelul funciei abort(). Programatorul poate schimba comportamentul funciei terminate() facnd ca aceasta s apeleze o alt funcie. Numele noii funcii va fi trimis ca parametru funciei set_terminate. Un catch care este urmat ntre parantezele rotunde de trei puncte trateaz toate excepiile. Exemplu catch(...) Aceast opiune este plasat, de obicei, ca o ultim variant ntr-o serie de blocuri catch. Un bloc try urmat de mai multe blocuri catch are un comportament similar instruciunii switch care folosete o ramur case n funcie de valoarea expresiei testate.

Generarea unei noi excepii n catch


Uneori, tratarea unei excepii nu poate fi fcut n blocul catch care a detectat-o i atunci se poate decide ca excepia s fie transmis mai departe, lasnd tratarea ei pe seama altei secvene de cod (rethrowing an exception). Instruciunea throw; lanseaz din nou excepia. Exemplu #include <iostream> using std::cout; using std::endl; #include <exception> using std::exception; void ThrowException() { //Lanseaza o exceptie si o prinde imediat try { cout << "Functia ThrowException" << endl; throw exception(); //Genereaza exceptia } catch(exception e) { cout << "Exceptia tratata in ThrowException" << endl; throw; } cout << "Acest text nu este tiparit"; } int main() { try { 6

Programarea calculatoarelor i limbaje de programare I

ThrowException(); cout << "Acest text nu este tiparit"; } catch(exception e) { cout << "Exceptia tratata in main" << endl; } cout << "Programul continua dupa catch in main" << endl; return 0; } Acest program afieaz: Functia ThrowException Exceptia tratata in ThrowException Exceptia tratata in main Programul continua dupa catch in main Funcia ThrowException este apelat n blocul try din main. n blocul try din funcia ThrowException este generat o excepie de tip exception care este detectat imediat de blocul catch asociat acestui try. Aici, excepia este relansat, fiind tratat n blocul catch din main.

Specificarea excepiilor
Lista excepiilor care pot fi generate de o funcie se poate specifica astfel: int g(double h) throw(a, b, c) { //corpul functiei } Prin aceast list se restrnge gama excepiilor care pot fi declanate de funcia g la tipurile a, b, c. Dac funcia genereaz o alt excepie dect cele listate, se apeleaz automat funcia unexpected din biblioteca standard care, la rndul ei apeleaz funcia terminate. Comportmenul funciei unexpected poate fi modificat asemntor modului n care se intervine asupra funciei terminate, dar apelnd, de data aceasta, funcia set_unexpected. O funcie pentru care nu exist o astfel de list poate genera orice excepie.