Sunteți pe pagina 1din 27

Tratarea erorilor

Universitatea Tehnic Gheorghe Asachi din Iai


Facultatea de Automatic i Calculatoare
PROGRAMARE ORIENTAT OBIECT
Curs 12
Introducere
Fie un program compus din mai multe pri/module
dezvoltate separat.
O parte a programului este apelat pentru a executa un
anumit task. Respectiva parte poate fi vzut ca o colecie
de funcii, o librrie.
O librrie este un cod obinuit dar n contextul gestiunii
erorilor proiectantul/dezvoltatorul (autorul) acelei librarii nu
cunoate din ce fel de programe va face parte librria
respectiv.
Astfel:
Autorul acelei librrii poate detecta erorile ce apar la rularea
programului (run time) dar, n general, nu tie ce decizie s ia.
Utilizatorul unei librrii poate gestiona o eroare de run time dar nu
o poate detecta uor
Modul traditional de gestiune al erorilor
1. Terminarea programului (abordare drastic).
Fie funcia:







Totui pentru majoritatea erorilor se poate face mai mult:
Afiarea/logarea unui mesaj de eroare inainte de a nchide
programul.



void *xmalloc(size_t size)
{
void *pv = malloc(size);
if(!pv)
{
exit(-1);
}
else
{
return pv;
}
}
cout << "Eroare alocare memorie"<<endl;
Modul traditional de gestiune al erorilor
n particular, o librrie nu tie care este scopul i strategia
programului n care este ncapsulat. Astfel, n cazul unei
erori, nu trebuie doar s apeleze exit() sau abort().
O librrie care nchide programul ntr-un mod necondiionat
nu poate fi utilizat ntr-un program care nu trebuie s
crape.

Modul traditional de gestiune al erorilor
2. Returnarea unui cod de eroare.
Nu este ntotdeauna convenabil deoarece unele funcii sunt
apelate n expresii a cror valoare urmeaz a fi
determinat.
Funcia poate fi modificat pentru a returna o pereche de
valori (o structur cu rezultatul funciei si un cod de eroare
ce caracterizeaz rezultatul). Dar acest lucru nu convine
deoarece pentru fiecare apel trebuie verificat codul de
eroare.
De asemenea, programatorii ignor sau uit s testeze
codul de eroare returnat. Exemplu, funcia printf()
returneaz o valoare negativ dac apare o eroare la
scrierea n stream. Acel cod de eroare niciodat nu este
testat.
Constructorii nu returneaz nimic.

double x = sqrt(15);
Modul traditional de gestiune al erorilor
3. Returneaz o valoare ns las programul ntr-o stare de
eroare
Funcia apelat nu notific n nici un fel programul despre
starea de eroare.
Foarte multe funcii din librria standard C seteaz variabila
global errno pentru a indica o eroare.

Valoarea variabilei x nu are sens avnd n vedere c
variabila errno a fost setat pentru a indica un argument
inacceptabil pentru funcia sqrt. Programele scrise nu
testeaz variabile errno.
Avnd n vedere existena variabilei globale errno pot s
apar probleme n prezena concurenei.
double x = sqrt(-1.0);
Modul traditional de gestiune al erorilor
4. Apelul unei funcii error-handler:



Intrebarea care apare: Ce face funcia
ErrorHandlerFunction().
Dac funcia nu rezolv complet problema atunci aceasta
trebuie s:
Termine programul
Returneze un cod de eroare
Seteaz o stare de eroare
Arunc o excepie
Dac funcia gestioneaz eroarea cu succes atunci de ce
este considerat eroare?
n mod tradiional astfel de abordri co-exist n programe

if(error)
{
ErrorHandlerFunction();
}
Excepii
Noiunea de excepie este furnizat pentru a obine
informaii din punctul unde este detectat o eroare n
punctul n care poate fi gestionat.
O funcie ce nu poate gestiona o problem, arunc o
excepie (throws) spernd ca apelantul (direct ori indirect)
s gestioneze problema.
O funcie care vrea s gestioneze o anumit problema
indic acest lucru prinznd excepia respectiv:
O component de apel indic tipurile de erori pe care le poate
gestiona specificnd excepiile respective n clauza catch a unui
bloc try.
Componenta apelat care nu ii poate duce taskul la bun sfrit
raporteaz eroarea aruncnd o excepie
Excepii
void Taskmaster()
{
try {
auto result = DoTask();
// ok
}
catch (someError)
{
// eroare in indeplinirea taskului. Gestioneaza.
}
}
int DoTask()
{
// ...
if (/*daca nu au aparut erori*/)
return result;
else
throw someError{};
}
Funcia TaskMaster ntreab DoTask() n legtur cu o
sarcin de indeplinit. Dac funcia DoTask() i-a ndeplinit
sarcina i ntoarce un rezultat corect, totul este bine. Altfel,
trebuie s raporteze erori aruncnd excepii.
TaskMaster() este pregtit s gestioneze eroarea aprut
dar totodat este posibil ca funia DoTask() s apeleze la
rndul ei alte funcii care la randul lor pot arunca excepii.
O altfel de eroare dect cea pentru care TaskMaster() este
pregtit indic un eec al funciei n indeplinirea sarcinei i
trebuie gestionat de secvena de cod ce a apelat
TaskMaster().
O funcie apelat nu trebuie doar s returneze o eroare a
aprut.
Pentru ca programul s fie funcional, o funcie apelat
trebuie s lase programul ntr-o stare bun fr scurgeri
de resurse.
Excepii
Mecanismul de gestiune a excepiilor:
Este o alternativ la tehnicile tradiionale atunci cnd acestea sunt
insuficiente, neelegante ori generatoare de erori.
Este complet; poate fi folosit la gestiunea tuturor erorilor detectate
Permite programatorului s separe codul de gestiune al erorilor de
restul codului.
O excepie este un obiect aruncat pentru a reprezenta
apariia unei erori. Poate fi de orice tip ce poate fi copiat dar
se recomand a se utiliza doar tipuri definite de utilizator
special pentru acel scop. Astfel se reduce ansa ca dou
librrii diferite s foloseasc acelasi cod de eroare pentru a
reprezenta erori diferite.
Cel mai simplu mod de a defini o excepie este de a defini o
clas special pentru acel tip de eroare
Excepii





O excepie poate purta informaii despre eroarea pe care o
reprezint

class RangeError {};

void f(int n)
{
if (n<0 || max<n) throw RangeError {};
// ...
}
Aruncarea i prinderea excepiilor
Pot fi aruncate excepii de orice tip cu condiia s
poate fi copiate
class NoCopy {
NoCopy(const NoCopy& x) = delete;//fara constructor de copiere
};
class MyError{
// ...
};
void f(int n){
MyError me;
switch (n){
case 0: throw me; // OK
break;
case 3: throw MyError{}; // valabil doar in C++11
break;
case 1: throw NoCopy{}; // eroare: nu se poate copia NoCopy
break;
case 2: throw MyError; // eroare: MyError este un tip
break;
}
}
Aruncarea i prinderea excepiilor
Obiectul excepie este o copie a celui aruncat. Astfel este
initializat o variabil temporar de tipul variabilei aruncate
cu variabila aruncat. Aceast variabil poate fi copiat de
cteva ori nainte de a fi prins: excepia este trimis napoi
de la funcia apelata la funcia apelant pn cnd un
handler potrivit este gsit.
Tipul excepiei este folosit pentru pentru a selecta un
handler al unui bloc try.
Informaia dintr-un obiect excepie este folosit n mod
obinuit pentru mesaje de eroare sau pentru help recovery
Procesul de trimitere a excepiei este numit stack
unwinding
Aruncarea i prinderea excepiilor
Avnd n vedere c o excepie este copiat de cteva ori
nainte de a fi prins, nu se pun informaii mari n ea.
Excepii ce conin cteva cuvinte sunt foarte comune.
Unele excepii nu poart nici o informaie; numele tipului
este suficient pentru a raporta o eroare.
noexception Function
Unele funcii nu arunc excepii iar unele chiar nu ar trebui.
Pentru a indica acest lucru se poate declara funcia
respectiv ca funcie noexcept

Este util pentru un programator deoarece nu trebuie sa mai
scrie blocul try/catch pentru apelul funciei respective i de
asemenea modulul de optimizare al compuilatorului nu mai
are n vedere toat calea petru gsirea unui handler.
Specificatorul noexcept spune doar c funcia nu
gestioneaz excepii.
double compute(double) noexcept;
Prinderea excepiilor
Fie:






Handlerul este invocat atunci cnd:
H este de acelai tip cu E
H este clasa de baz pentru E
H si E sunt tipuri de date pointeri ctre H sau E

void f()
{
try
{
throw E{};
}
catch(H)
{
//
}
}
Dac un handler prinde o excepie i decide c nu o
poate gestiona complet o poate rearunca.
void f()
{
try
{
throw E{};
}
catch(H)
{
if()
{

}
else
{
throw;
}
}
}
Prinderea oricrei excepii:

void f()
{
try
{
throw E{};
}
catch()
{
//
}
}
Handlere multiple
void f()
{
try
{
//
}
catch(H)
{
//
}
catch(F)
{
//
}
catch(G)
{
//
}
}
Exemple

double radical (double nr)
{
if (nr < 0.0)
throw "Nu pot extrage radicalul unui nr negativ!";
return sqrt (nr);
}
Exemple

int main ()
{
double nr;
cin >> nr;
try
{
cout << "Radical din " << nr << " este " <<
radical (nr);
}
catch (char *e)
{
cout << "Eroare: " << e << endl;
}
return 0;
}
Excepii netratate
Considerm urmtoarea funcie main pentru exemplul de
extragere a radicalului:





Funcia de extragere a radicalului ramne aceeai.
int main ()
{
double nr;
cin >> nr;

cout << "Radical din " << nr<< " este " << radical (nr);
return 0;
}
double radical (double nr)
{
if (nr < 0.0)
throw "Nu pot extrage radicalul unui nr negativ!";
return sqrt (nr);
}
Excepii netratate
La introducerea unei valori negative, rularea programului se
va opri brusc i va fi semnalat o eroare:

Funciile pot lansa excepii care pot s nu aib nici o
modalitate de tratare a lor
Pentru a preveni aceste situaii, se poate introduce un
handler catch-all:
try
{
cout << "Radical din " << nr <<" este " << radical (nr);
}
catch (int e)
{
cout << "Eroare: " << e << endl;
}
catch (...)
{
cout << "Eroare necunoscuta" << endl;
}
Excepii n funcii membre
Se folosesc
n special n constructori pentru a semnala unele erori la
crearea obiectelor
Cnd se dorete semnalarea unei erori fr a duce la
terminarea programului
Excepii standard
Librria c++ standard furnizeaz o clas de baz pentru
tratarea obiectelor furnizate ca excepii
Aceast clasa este std::exception
Conine o funcie virtual what ce returneaz un char *
int main()
{
try {
while (true) {
new int[100000000ul];
}
} catch (const std::bad_alloc& e) {
cout << "Allocation failed: " << e.what() << '\n';
}
}
http://en.cppreference.com/w/cpp/memory/new/bad_alloc

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