Sunteți pe pagina 1din 6

Tratarea excepţiilor.

1. Tratarea erorilor în C.
2. Tratarea excepţiilor în C++.
3. Zone de nume.
1. Tratarea erorilor în C.
În C există două moduri de tratare a erorilor la execuţie:
 cu reluare – la apariţia erorii se execută o secvenţă de instrucţiuni, având ca
scop eliminarea erorii, după care se continuă programul
 tratarea erorilor fatale – constând în suspendarea execuţiei programului, cu
întoarcerea unui cod de eroare la procesul părinte.
Funcţiile void exit(int) şi void abort(void), declarate în
<stdlib.h> suspendă execuţia programului din care sunt lansate.
Apelul funcţiei exit() : eliberează zonele tampon alocate, închide fişierele
deschise, apelează destructorii obiectelor statice utilizate, suspendă aplicaţia,
transmiţând parametrul din apel procesului părinte, interpretat de acesta ca un cod de
eroare dacă are valoare nenulă (EXIT_FAILURE); o valoare nulă reprezintă o
terminare normală a aplicaţiei (EXIT_SUCCES). Un efect identic se obţine prin
execuţia instrucţiunii return cod_er; lansată din funcţia int main().
Înaintea eliberării resurselor şi suspendării execuţiei programului prin
exit() este posibil să executăm mai multe funcţii, care au fost înregistrate prin
apeluri ale funcţiei atexit(). Aceasta are ca parametru un pointer la funcţia
înregistrată. Exemplu:
#include <stdlib>
void f(){. . .};
void g(){. . .};
void main(){
atexit(g); //inregistrarea functiilor care se executa
atexit(f); //in ordinea f(), g() la terminare normala
//sau anormala a programului
. . .
exit(1); //se executa f(), g() inainte de suspendare
. . .
//si aici se executa f(), g() la terminare normala
}
Apelul funcţiei abort() întrerupe imediat aplicaţia, fără eliberarea
resurselor şi apelarea destructorilor obiectelor statice, cu returnarea codului de eroare
3 şi afişarea mesajului “abnormal program termination”.
Utilizarea macroinstrucţiunii assert(condiţie) , definită în <assert.h>
permite testarea validităţii unor condiţii. Valoarea 0 a condiţiei suspendă execuţia
programului şi afişează mesajul: “Assertion failed (conditie)
__FILE__ ff __LINE__ ll”. Această macroinstrucţiune este folosită mai
ales în faza de depanare a programelor.

1
Pentru a elimina secvenţele assert() după depanarea programului, se
defineşte macroinstrucţiunea assert() în mod condiţionat de prezenţa unei
constante simbolice NDEBUG.
#define NDEBUG
#include <assert.h>
. . .

2. Tratarea excepţiilor în C++.


O excepţie reprezintă o condiţie de eroare, care transmisă sistemului determină
producerea unei erori la execuţie. De exemplu: o înpărţire prin 0, ieşirea indicelui în
afara limitelor unui tablou, etc.
La apariţia unei excepţii, utilizatorul îşi poate defini o secvenţă proprie de
tratare a acesteia.
În C++ se utilizează un model ierarhic de tratare a excepţiilor. Acesta foloseşte
3 cuvinte cheie: try – detectare, catch – tratare, throw – activare.
Transmiterea informaţiilor se va face prin intermediul unor obiecte excepţii.
Cuvântul try delimitează un bloc din programul utilizatorului, care va fi
supravegheat pentru a detecta apariţia unor excepţii. În cazul apariţiei unui anumit tip
de excepţie, este pasată excepţia sesizată prin throw(obiect_exceptie) rutinei
de tatare corespunzătoare, care începe prin catch(obiect_exceptie).
Excepţia poate fi captată, specificând numai tipul ei, prin
catch(tip_exceptie). La apariţia excepţiei este construit un obiect având
tipul excepţiei corespunzătoare şi se activează excepţia. Membrii obiectului servesc la
identificarea naturii excepţiei.
Rutina de tratare a excepţiei urmează imediat după blocul try:
catch(tip_exceptie){
// rutina de tratare a exceptiei;
}
Să considerăm două excepţii, care pot să apară în cazul folosirii vectorilor:
 ieşirea indicilor în afara limitelor vectorului - clasa excepţie Range
 construirea unui vector cu dimensiune care depăşeşte dimensiunea maximă
admisă – clasa excepţie Size
Primul tip de excepţie apare la indexare, deci excepţia va fi activată în funcţia
operator de indexare.
A doua excepţie apare la declararea unui obiect vector, deci excepţia va fi
activată în constructorul clasei vector.
Clasele excepţii vor fi declarate drept clase incluse în clasa Vector:
class Vector{
float* v;
int d; // numar de elemente vector
enum{DMAX=100}; // dimensiune maxima vector
public:
class Range{}; // clase exceptii

2
class Size {};
Vector(int n); // constructor
float& operator[](int i);//functie operator de indexare
};
Vector::Vector(int n){
if(n < 0 || n >= DMAX)
throw Size();
d = n;
v = new float[n];
};

float& Vector::operator[](int I){


if(i >= 0 && i < d)
return v[i];
throw Range();
return v[0];
};

void main(){
try{
Vector x[200]; // declanseaza exceptia Size
int i=150;
x[i] = 1.5; // declanseaza exceptia Range
. . .
};
catch(Vector::Size){
. . .
// rutina de tratare exceptie Size
. . .
};
catch(Vector::Range){
. . .
// rutina de tratare exceptie Range
. . .
};
}

3. Zone de nume.
În programele mari, scrise de mai mulţi programarori, partajate în mai multe
fişiere sursă, riscul conflictelor de nume (de variabile, funcţii, clase, obiecte) este
ridicat.
O regiune declarativă a unui program este o zonă în care se fac declaraţii.
Astfel regiunea declarativă a unei variabile locale o reprezintă funcţia, iar a unei
variabile globale este fişierul în care este declarată.
Domeniul potenţial de vizibilitate al unui nume se întinde de la declararea
acestuia până la sfârşitul regiunii declarative.
O variabilă poate să nu fie văzută peste tot în domeniul potenţial de
vizibilitate. Ea poate fi ascunsă de către un altă variabilă cu acelaşi nume, declarată
într-o regiune declarativă imbricată.
Domeniul de vizibilitate reprezintă zona în care variabila poate fi accesată.

3
O zonă de nume este un mecanism de exprimare a unei grupări logice.
Declararea zonei de nume se face printrr-o interfaţă de forma:
namespace nume{
declaraţii entităţi (variabile, funcţii, etc)
};
De exemplu, o zonă de nume FunMatGrade, în care se declară funcţii
trigonometrice cu argumentul exprimat în grade, care păstrează aceleaşi nume cu cele
din fişierul antet <math.h> este:
namespace FunMatGrade {
double sin(double);
double cos(double);
. . .
};
Definirea zonei de nume, este de obicei separată de declarare, fiind situată în
partea de implementare.
În definiţie, numele definite sunt calificate cu numele zonei, folosind
operatorul de rezoluţie.
Astfel numele din zona de nume dată mai sus se definesc prin:
double FunMatGrade :: sin(double){/* . . . */};
double FunMatGrade :: cos(double){/* . . . */};
. . .
Accesul la un nume din zona de nume se face de asemeni calificat. Exemplu:
y = FunMatGrade :: sin(x);
Aceste notaţii lungi şi greoaie pot fi evitate folosind declaraţii de utilizare.
Acestea sunt formate din using şi numele calificat, care în continuare poate fi folosit
necalificat. Astfel:
using FunMatGrade :: sin(double);
ne permite să apelăm funcţia definită pentru calculul sinusului prin y=sin(x).
Pentru a putea folosi funcţia obişnuită de calcul a sinusului, declarată în <math.h> ,
vom preceda numele cu operatorul de rezoluţie, adică y= :: sin(x).
Aceasta reprezintă zona de nume globală şi corespunde regiunii declarative la
nivel de fişier.
Zonele de nume sunt deschise, adică putem adăuga nume la zone existente.
Declaraţia de utilizare adaugă un nume la regiunea declarativă locală,
împiedicând declararea altei variabile locale cu acelaşi nume (şi ascunzând variabila
globală cu acelaşi nume).
Exemplu:
namespace A{
int x;
void f();
};

4
char x; //nume global
void main(){
using A::x; //x din zona de nume considerat local
double x; //eroare, exista x local din zona de nume
cin >> ::x; //citeste x global
}
Directiva de utilizare using namespace A; aplică declaraţia de utilizare
tuturor numelor din zona de nume, care devin locale în zona curentă. De exemplu:
#include <iostream>
using namespace std;
face ca toate numele din zona de nume std să devină globale.
Dacă un nume este declarat local unei funcţii, el nu poate fi importat cu o
declaraţie de utilizare. Numele poate fi importat cu o directivă de utilizare, dar va fi
ascuns de către numele local. Astfel:

char x; //declarat global


void main(){
double x; //declarat local;
using namespace A; //se accepta,dar using A::x
//este ilegal
cin >> x; //x local, celelalte sunt ascunse
cin >> ::x; //x global
cin >> A::x; //x din zona de nume
}
Dacă nu folosim directiva de utilizare, vom adopta una din variantele:
#include <iostream>
. . .
int x;
std::cin >> x;
std::cout << x << std::endl;
respectiv:
#include <iostream>
. . .
using std::cin;
using std::cout;
using std::endl;
int x;
cin >> x;
cout << x << endl;
O zonă de nume poate fi inclusă în altă zonă de nume. În acest caz accesul la
numele declarate în zona interioară se face prin dublă calificare:
namespace A{
namespace B{
double b;
};
int a;

5
}
Calificarea A::B::b poate fi evitată cu declaraţia de utilizare using
A::B.
Pentru a crea un sinonim B al unei zone de date A scriem namespace B=A;
Directiva de utilizare este tranzitivă:
namespace A{
int x;
}
namespace B{
using A::x;
. . .
}
ne permite să accesăm pe x prin A::x sau B::x.
Toate clasele, obiectele şi funcţiile din biblioteca standard C++ se definesc în
zona de nume std.
Fişierele antet au aceleaşi nume cu fişierele antet corespunzătoare anterioare,
dar le lipseşte extensia .h.

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