- •Programare orientată pe obiecte
- •§1. Principiile programării orientate pe obiecte.
- •§3. Constructori şi destructori.
- •8) Aceste mesaje apar după ce se termină lucrul programului.
- •§4. Operaţii de intrare/ieşire a informaţiei.
- •§5. Moştenire simplă.
- •§6. Moştenire multiplă
- •§7. Moştenirea pe mai multe niveluri. Clase virtuale
- •§9. Definirea şi utilizarea referinţelor
- •§10. Tablouri de obiecte. Pointeri şi referinţe la obiecte. Pointeri la membrii clasei
- •§11. Dirijarea dinamică a memoriei
- •§12. Constructor de copiere
- •§13. Funcţii prietene şi clase prietene
- •§14. Supraîncărcarea operatorilor
- •§15. Supraîncărcarea operatorilor prin funcţii prietene
- •§16. Supraîncărcarea unor operatori speciali
- •Operatorii de incrementare şi decrementare
- •Operatorii de inserţie şi extragere
- •Operatorul indice
- •Operatorul funcţie
- •Operatorii new şi delete
- •Operatorul virgulă
- •Operatorul de conversie
- •§17. Funcţii-şablon şi clase-şablon
- •§18. Realizarea conceptului de polimorfism
- •§19. Clase abstracte
- •§20. Membrii statici ai clasei
- •§23. Tratarea excepţiilor
- •Bibliografie
§23. Tratarea excepţiilor
Un program elaborat corect înseamnă nu doar prelucrări ale datelor corecte, ci şi prelucrarea datelor eronate. Scenariile descrise de programe trebuie să conţină fragmente care ar prelucra şi date generatoare de erori. Procesul de prelucrare a posibilelor erori este unul foarte anevoios: trebuie evidenţiate situaţiile generatoare de erori, iar apoi trebuie de prelucrat fiecare situaţie în parte. Situaţiile generatoare de erori sunt, de fapt nişte situaţii excepţionale şi de aceea prelucrarea erorilor poate fi numită şi tratare a excepţiilor.
Limbajul C++ are un mecanism propriu de tratare a excepţiilor. Acest mecanism se bazează pe conceptul de clasă. Deci o excepţie este descrisă de o clasă, având o serie de proprietăţi şi de operaţii caracteristice. Din punctul de vedere al programării, a genera o excepţie înseamnă a crea un obiect cu anumite proprietăţi şi operaţii concrete, care descrie un anumit tip de erori.
Pentru prelucrarea excepţiilor, există trei construcţii specializate: try{…}, throw şi catch(){…}. Fiecare dintre construcţiile date îşi are rolul său specific în evidenţierea şi prelucrarea excepţiilor. Blocurile de instrucţiuni suspectate de a putea genera erori sunt plasate între acoladele instrucţiunii try, adică formează corpul ei:
try
{
instr_1;
instr_2;
. . .
instr_k;
}
Dacă valorile datelor sunt în contradicţie cu modul lor de prelucrare, trebuie de generat o excepţie. Generarea unei excepţii este realizată cu ajutorul instrucţiunii throw. Instrucţiunea dată are ca parametru un obiect de tipul ce corespunde excepţiei. De aceea poate fi scris
throw obiectExceptie;
unde obiectExceptie este un obiect creat în baza clasei ce corespunde excepţiei. Deseori obiectul dat este creat chiar în cadrul instrucţiunii throw cum urmează în continuare
throw numeExceptie(lista_par);
unde numeExceptie este numele constructorului clasei care descrie excepţia, iar lista_par este lista parametrilor reali utilizaţi la generarea obiectului-excepţie. Deci numeExceptie este un tip de date, fie predefinit, fie definit. În cazul când este un tip predefinit, lista_par constă dintr-o singură valoare şi, de regulă, este omis din construcţie numele tipului predefinit, rămânând doar valoarea. De exemplu, poate fi scris
throw int(3);
ceea ce este echivalent cu
throw 3;
Procesul de generare a excepţiei se numeşte lansarea excepţiei.
Pentru a prelucra excepţiile lansate, după blocul try sunt plasate o serie de construcţii de tip catch:
catch(tip [nume_ob])
{
instr_1;
instr_2;
. . .
instr_k;
}
unde tip este un tip de date predefinit sau definit de utilizator, care descrie tipul excepţiei, iar nume_ob este numele obiectului, care reprezintă excepţia şi care poate lipsi. Deci în acest caz toate excepţiile de acest tip vor fi tratate identic. Dacă se doreşte existenţa unei construcţii catch care ar prelucra orice tip de excepţii, în locul parametrului sunt plasate trei puncte (. . .):
catch(. . .)
set_instructiuni
În acelaşi loc al programului pot fi, de regulă, plasate mai multe construcţii catch, obţinând un pachet de astfel de construcţii. De aceea într-un astfel de pachet trebuie respectate o serie de reguli:
există o singură excepţie care se referă la un tip de date anume;
nu are vreo importanţă ordinea de amplasare a excepţiilor care se referă la diferite tipuri de date;
excepţia care se referă la toate tipurile de date, adică cea care conţine trei puncte (. . .)este plasată ultima în secvenţa instrucţiunilor catch.
În continuare, este prezentat un exemplu, excepţiile fiind de tipuri predefinite de date. Excepţiile sunt lansate în funcţia f(). După blocul try sunt plasate trei blocuri catch(): unul pentru excepţii de tip int, altul pentru excepţii de tip double şi ultimul pentru prelucrarea excepţiilor de orice alt tip.
#include<iostream.h>
void f(int i)
{
if(i<0)
throw -1;
if(i>=0 && i<100)
throw 100.0;
if(i>=1000)
throw float(1000);
cout<<i<<endl;
}
//--------
void main()
{
int i;
cin>>i;
try
{
f(i);
}
catch(int i)
{
cout<<i<<"-exceptie tip int\n";
}
catch(double)
{
cout<<"exceptie tip double\n";
}
catch(...)
{
cout<<"exceptie generala\n";
}
}
Fiindcă o excepţie este în caz general reprezentată printr-o clasă, urmează un exemplu de excepţie definită pentru a prelucra situaţia de inexistenţa unui fişier deschis pentru anumite operaţii.
#include<fstream.h>
#include<iostream.h>
#include<conio.h>
class FisierInexistent{};
void main()
{
char numePrenume[100];
try
{
ifstream cit("persoane.txt");
if(cit.fail())
{
throw FisierInexistent();
}
cit.getline(numePrenume,100);
cout<<numePrenume<<endl;
}
catch(FisierInexistent e)
{
cout<<"Eroare de deschidere: fisier inexistent";
}
getch();
}
Dacă fişierul deschis nu există, va fi lansată excepţia FisierInexistent şi ca rezultat al prelucrării excepţiei date va fi afişat mesajul ”Eroare de deschidere: fisier inexistent”.
Clasa care reprezintă excepţia FisierInexistent este una foarte simplă, ne-conţinând nici un membru. Totuşi, ea poate fi proiectată, astfel încât să aibă o structură mai complexă. Poate conţine în calitate de informaţie numele fişierului, poate avea o serie de funcţii membru, de exemplu, constructori sau alte funcţii implicate în prelucrarea situaţiei excepţionale. Exemplul precedent va fi re-scris desfăşurând puţin clasa FisierInexistent.
#include<fstream.h>
#include<iostream.h>
#include<conio.h>
class FisierInexistent
{
char numeFisier[50];
public:
FisierInexistent(char *nume)
{
strcpy(numeFisier, nume);
}
void MesajEroare()
{
cout<<”Eroare: fisierul \””<<numeFisier
<<”\” nu poate fi gasit!”<<endl; }
};
void main()
{
char numePrenume[100];
char numef[]=”persoane.txt”;
try
{
ifstream cit(nume);
if(cit.fail())
{
throw FisierInexistent(nume);
}
cit.getline(numePrenume,100);
cout<<numePrenume<<endl;
}
catch(FisierInexistent e)
{
e.MesajEroare();
}
getch();
}
Din aceste exemple se văd doar unele caracteristici ale excepţiilor. Pentru a putea aprecia puterea excepţiilor, trebuie de ţinut cont că un algoritm serios poate consta dintr-o ierarhie de apeluri de funcţii. Eroarea poate surveni în orice punct al ierarhiei şi în acest caz este necesară o terminare corectă a proceselor lansate. O latură forte a mecanismului excepţiilor este anume terminarea corectă a proceselor lansate.
Este de menţionat faptul că construcţiile de tip try pot fi imbricate, adică incluse unele în altele. Cea mai simplă ierarhie de imbricare de acest tip ar consta din două niveluri şi ar putea avea următoarea reprezentare generalizată:
try
{
//sectorul A inceput
. . .
//sectorul A sfirsit
try
{
//sectorul B inceput
. . .
//sectorul B sfirsit
}
//sectorul C inceput
catch( )
{
. . .
}
. . .
//sectorul C sfirsit
//sectorul D inceput
. . .
//sectorul D sfirsit
}
//sectorul E inceput
catch( )
{
. . .
}
. . .
//sectorul E sfirsit
O astfel de construcţie are mai multe scenarii de execuţie care depind de locul lansării excepţiei şi de tipul excepţiilor interceptate, care formează blocul de prelucrare intern şi blocul de prelucrare extern. Dacă excepţia este lansată în sectorul A sau D, atunci execuţia este transferată în sectorul E unde va fi selectată, dacă există, prelucrarea necesară după tipul ei. Dacă excepţia este lansată în sectorul B, atunci execuţia este transferată în sectorul C unde va fi selectată, dacă există, prelucrarea necesară după tipul ei, iar apoi se va trece la sectorul D. Dacă în sectorul C nu există blocul necesar de prelucrare, atunci se va trece la sectorul E, încercându-se găsirea blocului necesar de prelucrare aici. Este interesant faptul că o excepţie poate fi lansată şi în sectorul C în cadrul unuia dintre blocurile catch(). Poate fi lansată o excepţie nouă sau poate fi relansată excepţia veche. O relansare a excepţiei poate fi făcută pentru a schimba într-un fel scenariul de prelucrare a ei. La lansarea excepţiei în sectorul C execuţia va fi direcţionată în sectorul E.
