Skip to content

Latest commit

 

History

History
105 lines (102 loc) · 6.41 KB

POO_22_C_2022-03-17.md

File metadata and controls

105 lines (102 loc) · 6.41 KB

TRATAREA EXCEPTIILOR

Exceptiile sunt niste situatii deosebite ce pot aparea pe parcursul executiei unei aplicatii. Exista exceptii sincrone si exceptii asincrone. Exceptiile sincrone sunt usor de detectat: impartire prin 0, un fisier nu a putut fi deschis, etc. Exceptiile asincrone sunt mult mai greu de detectat si de tratat. Exceptiile sunt in general erori ce pot aparea in timpul functionarii unui program. O data detectate, exceptiile trebuie tratate. Exemple de masuri:

  • se afiseaza mesaje pe ecran sau in log
  • se reincearca executia unei secvente de program sau se reporneste executia In limbajul C, tratarea exceptiilor poate fi facuta cu ajutorul unor functii din fisierul antet setjmp.h. Pentru tratarea exceptiilor avem nevoie de tipuri de date jmp_buf, functia setjmp si functia longjmp. Intr-o variabila de tip jmp_buf poate fi incarcata starea programului din momentul apelului functiei setjmp. Prin starea programului intelegem stiva de executie a aplicatiei. La primul apel al functiei setjmp, functia returneaza valoarea 0. Cu ajutorul functiei longjmp ne putem intoarce la linia in care setjmp a salvat starea executiei, moment in care se reincarca starea executiei salvata. Cu alte cuvinte, apelul longjmp este echivalent cu incarcarea stivei de executie dintr-o variabila jmp_buf si un goto la linia in care setjmp a salvat starea programului. Cu ajutorul acestor doua functii este simulata functionalitatea unei instructiuni de tipul try catch impreuna cu throw, care nu exista in C.
jmp_buf stare;
float div(float x, float y)
{
	if(!y)
		long_jmp(stare,1); // throw
	else
		return x/y;
}

void main()
{
	float a,b;
	cin>>a>>b;
	if(!setjmp(stare))
	{
		cout<<"Impartire = "<< div(a,b) << endl;
	}
	else
		{
			cerr<"Exceptie"<<endl;
		}
}

La executie, cand se ajunge prima oara la functia setjmp este salvata starea programului. Functie setjmp returneaza 0 inseamna ca se trece la afisarea cu cout. Daca in functia div apelata, al doilea parametru are valoarea 0 executia se incheie in functia div , se incarca starea salvata si se face un salt la linia in care este apelata setjmp. La al doilea apel al functiei setjmp este returnata valoarea 1 si executia continua pe ramura else cu mesajul de eroare. In limbajul C++ putem trata exceptiile cu ajutorul functiilor mostenite din C, dar, in C++ au fost introduse doua instructiuni pentru tratatea exceptiilor: throw si try ... catch . La detectarea unei exceptii, instructiunea throw intrerupe executia si arunca o valoare ce este captata in functie de tipul ei pe una dintre ramurile catch daca exceptia a aparut in blocul try. Vrem sa calculam loga(b):

double logaritm(double x, double y) // logx(y)
{
	if(x<=0)
		throw 1;
	if(y<=0)
		throw 2;
	if(x==1)
		throw "Baza nu poate fi 1.";
	return log (y) / log (x);
}

void main()
{
	double a,b;
	cin>>a>>b;
	try
	{
		cout<<"log = "<<logaritm(a,b)<<endl;
	}
	catch(int v)
	{
		if(v==1) 
			cerr<< "Baza nu este pozitiva.";
		if(v==2)
			cerr<< "Argumentul nu este pozitiv";
	}
	catch(const char* msg)
	{
		cerr<<msg<<endl;
	}
	catch(...)
	{
		cerr<< "Alta eroare"<<endl;
	}
}

Dupa citirea valorilor a si b se incearca executia codului din blocul try. Daca nu au aparut exceptii (aruncate cu o instructiune throw), mesajul este trimis in cout (pe ecran). Daca in blocul try apare o exceptie, executia se incheie, mesajul neafisandu-se, iar executia continua pe una dintre ramurile catch. Daca x<=0 sau y<=0 atunci este aruncata o valoare de tip int, (1 sau 2), in consecinta executia este intrerupta si se revine la prima instructiune try...catch, unde executia continua pe ramura catch cu parametru int, deoarece valoarea aruncata este de tipul int (1 sau 2). Daca x=1 executia se incheie aruncandu-se un mesaj, practic, aruncandu-se adresa de memorie a mesajului respectiv. Executia continua in aceasta situatie pe ramura catch cu valoare const char*, unde se primeste adresa de memorie a textului aruncat. In functie de tipul valorii aruncate de o instructiune throw si de tipurile de date ale ramurilor catch se incearca potrivirea cea mai buna. (short int -> int, int -> float). Daca valoarea aruncata nu poate fi convertita la niciunul din tipurile ramurilor catch, executia continua pe ramura catch(...) , daca exista, unde este tratata sau exceptia ramane netratata, rezultand eroare la executie: Unhandled exception.. Exista, in functie de compilator, si alte posibile ramuri ale instructiunii try...catch. De exemplu, Visual C++ putem sa avem ramuri de tip except sau final, dar nu sunt standard. In Java exista final standard. Instructiunea throw poate fi apelata fara valoare. La intalnirea unui throw fara valoare, daca exista o valoare aruncata si inca netratata, ea este aruncata pentru a fi tratata in urmatoarea instructiune try...catch.

float a,b;
cin>>a>>b;
try
{
	try
	{
		if(!b)
			throw 0;
		cout<<a<<b<<endl;
	}
	catch(int v)
	{
		cerr<< "Eroare"<<endl;
		throw;
	}
}
catch(int v)
{
	cerr<< "Impartire la 0."<<endl;
}

La a doua instructiune try...catch, daca se intalneste situatia b=0 se decide ca exceptia sa nu fie tratata aici, aruncandu-se tratarea ei intr-o instrunctiune try...catch de mai sus, adica, pe ramura catch a primei instructiuni try...catch. Aruncarea tratarii pe un nivel superior se face cu throw;. Desi tratarea exceptiilor cu ajutorul instructiunilor try...catch este moderna si eleganta, codul din corpul try se executa mai incet, comparativ cu situatia in care nu ar fi introdus intr-un astfel de corp. De aceea, folosirea instructiunilor try...catch este preferabil de a fi editata, acolo unde avem nevoie de viteza mare de executie. Foarte multi programatori de C++ evita complet utilizarea instructiunilor try...catch. In momentul in care se scrie o aplicatie cu tratare de exceptii se prefera scrierea de clase speciale pentru exceptii, chiar ierarhii. Intr-u obiect de tip exceptie se memoreaza detalii despre exceptia respectiva in mai multe campuri. In alte limbaje, precum Java, aceasta practica este absolut fireasca tocmai din cauza ca Java se doreste a fi un limbaj sigur.