680 likes | 832 Views
Tratarea Exceptiilor.Spatii de nume. I/O. Curs 12-13 . Tratarea erorilor. C : int printf ( const char * format, ... ); Return Value: On success , the total number of characters written is returned. On failure , a negative number is returned.
E N D
Tratarea Exceptiilor.Spatii de nume. I/O Curs 12-13
Tratarea erorilor • C : • int printf ( const char * format, ... ); • Return Value: • On success, the total number of characters written is returned.On failure, a negative number is returned. • Rareori programatorii verifica codul de retur • In cazul in care o fac, codul este o succesiune de if-uri care verifica variatele posibilitati • Problema majora – cuplarea – dependenta – gradul in care un modul depinde de un alt modul
Tratarea erorilor in C • Valoare de retur care indica o problema • Setarea unui flag care sa indice existenta unei erori : errno, perror() • errno – variabila intreaga declarata in <errno.h>, folosita de numeroase functii pentru a returna coduri de eroare • void perror ( const char * str ) (print error message) • Interpreteaza valoare lui errno si o converteste la un string pe care il afiseaza pe stderr (de obicei ecran) • perror () trebuie apelata chiar dupa aparitia erorii, altfel poate fi suprascrisa de alte functii • Folosirea setjmp() si longjmp() • setjmp() salveaza o stare normala a programului • In situatii de eroare se apeleaza longjmp() care restaureaza acea stare buna • NU apeleaza DESTRUCTORII!!! (folosit in C++)
Errno+perror #include <stdio.h> #include <errno.h> int main () { FILE * pFile; pFile=fopen ("unexist.txt","rb"); if (pFile==NULL){ printf("errno = %d\n", errno); perror ("The following error occurred"); } else fclose (pFile); return 0; } Cod de retur char *c = new char[200000000] if ( c == NULL ) cout<<“eroare"; else{ //procesare informatii … delete c; } Tratarea erorilor C
Sabloane GRASP • GRASP – General Responsibility Assignment Patterns (Principles) • Low coupling – dezvoltarea de module independente • dependenta scazuta intre module, • modificari asupra unui modul sa nu determine modificari majore asupra altor module • High Cohesion • un modul trebuie sa aiba o multime de responsabilitati relationate • Utilizarea claselor oera suport pentru coeziunea unui proiect • Controller • Delegarea sarcinilor de tratare a evenimentelor din interfata grafica unei clase cu rol de tratare a evenimntelor • Polimorfism – variatiile comportamentelor pe baza tipurilor de date sunt tratate de tipul de date, folosind apeluri polimorfice
Tratarea erorilor in C • Codul de tratare a erorilor este puternic cuplat cu codul logicii aplicatiei • La apelul unei functii care returneaza un cod de eroare, inconjuram codul care face apelul de cod de testare a modului de finalizare a apelului • NULL si -1 (valori negative) folosite pentru a indica esecul unui apel – trebuie testate imediat dupa apel • La folosirea setjmp si longjmp exista o puternica cuplare intre codul unde se slaveaza starea normala si codul in care apare eroarea
Abordarea C++ • Tratarea exceptiilor: • Codul de tratare a erorilor este separat de codul care trateaza logica aplicatiei • Intai scriem codul care dorim sa se execute, apoi separat, se scrie codul care se ocupa de tratarea situatiilor neprevazute • Daca se fac apeluri multiple ale aceleiasi functii, erorile returnate de acea functie sunt tratate o singura data, intr-un singur loc • Erorile nu mai pot fi ignorate. • Daca o functie trebuie sa transmita un mesaj de eroare apelantului, acea functie va arunca(throw) un obiect reprezentand eroarea in afara functiei. • Daca apelantul nu prinde(catch) eroarea si nu o trateaza, acest obiect este aruncat mai departe pana cand fie este tratat, fie programul se termina deoarece nu a existat nici un handler pentru eroare.
Daca in cod apare o situatie exceptionala si nu exista suficienta informatie pentru a fi tratata, informatia poate fi transmisa mai departe, intr-un context unde situatia poate fi rezolvata Transmiterea informatiei se realizeaza prin intermediul unui obiect care contine informatie relevanta despre situatia intalnita Obiectul este aruncat in afara contextului = aruncarea unei exceptii MyException.h #ifndef MY_EXCEPTION_H #define MY_EXCEPTION_H class MyException{ const char* err_msg; public: MyException(const char* const m):err_msg(m) { } }; #endif Test.cpp #include "MyException.h" void f(){ throw MyException("Mesaj");} int main(){ f(); return 0; } Aruncarea unei exceptii
Aruncarea unei exceptii • Throw: • Se creeaza o copie a obiectului aruncat • Se returneaza aceasta copie din functia in care a fost intalnit, chiar daca functia nu returna tipul de data al exceptiei in mod natural • Executia continua intr-o parte speciala a codului – handlerul exceptiei • Toate obiectele locale create in scopul in care a fost aruncata exceptia sunt distruse (“stack unwinding”) • Se pot arunca oricate tipuri de exceptii este necesar • In mod obisnuit, pentru fiecare tip de anomalie se va arunca un tip de exceptie diferit • Informatia este salvata in obiect, iar numele clasei exceptie trebuie sa reflecte anomalia (Java: IOException, ClassNotFoundException, NullPointerException, IndexOutOfBoundsException, FileNotFoundException, etc)
Aruncarea unei exceptii • Se poate arunca orice tip de data (inclusiv tipuri de date predefinite), dar de obicei se creeaza clase speciale
Prinderea unei exceptii • Se realizeaza in interiorul unui handler de exceptie • Este nevoie de cate un handler de exceptie pentru fiecare tip de exceptie care se doreste a fi prinsa • Daca in interiorul unei functii se arunca o exceptie sau se apeleaza o functie care arunca o exceptie, corpul functiei este parasit din cauza exceptiei aruncate • Daca se doreste ca aparitia lui throw in interiorul functiei sa duca la parasirea corpului functiei – bloc special in care se incearca rezolvarea problemei – blocul try • Blocul try-catch – domeniu de vizibilitate normal try{ //cod susceptibil (de exceptii) }
Tratarea exceptiilor • Handlerii de exceptii apar imediat dupa blocul try • Marcati prin cuvantul cheie catch Similar argumentelor unei functii try{//cod susceptibil …. }catch(Tip_1 id_1){//handler de exceptie Tip_1} catch(Tip_2 id_2) {//handler de exceptie Tip_2} … catch(Tip_n id_n) {//handler de exceptie Tip_n}
Exceptii • 3 participanti la aruncarea/tratarea unei exceptii: • Blocul try – marcheaza codul care ar putea sa genereze situatii de eroare • Clauza catch – urmeaza imediat blocului try si contine codul care trateaza problema • Instructiunea throw – modul in care codul cu probleme notifica codul apelant legat de aparitia unei probleme
Alocarea memoriei cu tratarea exceptiilor #include <iostream> using namespace std; int main(){ int* buf; try{ buf=new int[256]; if (buf==NULL) throw "Alocarea memoriei esuata!"; else delete [] buf; } catch (char* exc){ cerr<<exc<<endl; } return 0; } Blocul TRY Instructiunea THROW Blocul CATCH
Constructori si destructori • Controlul trece de la punctul in care apare THROW la un handler • Obiectele create in scopul in care a fost aruncata exceptia sunt distruse AUTOMAT prin apelul destructorilor • Daca constructorul unui element al unui vector arunca o exceptie, numai elementele construite ale vectorului vor fi distruse
Potrivirea exceptiilor • La aruncarea unei exceptii, se cauta cel mai “apropiat” handler in raport cu ordinea in care acestia apar in cod • La gasirea unei potriviri, cautarea se opreste • Polimorfismul functioneaza • E indicat sa prindem o exceptie prin referinta, pentru a evita apelul constructorului de copiere sau folosind pointeri • Nu se fac conversii automate de tip in procesul de potrivire a exceptiilor
Conversii automate #include <iostream> using namespace std; class Exc1{ }; class Exc2{ public: Exc2(Exc1){} }; void f(){ throw Exc1(); } int main(){ try{ f(); }catch(Exc2& e){cout<<"Exceptia 2"<<endl;} catch(Exc1& e){cout<<"Exceptia 1"<<endl;} return 0; } Constructor de conversie Se executa deoarece NU se fac conversii automate de tip
Prinderea oricarui tip de exceptie • Pentru a prinde orice tip de exceptie, se poate crea un handler care trateaza orice tip de exceptie folosind … pe postul listei de argumente • catch(…){cout<<“orice exceptie “<<endl;} • Este recomandat ca clauza catch(…) sa fie pusa ultima in lista handlerilor
MyException.h #ifndef MY_EXCEPTION_H #define MY_EXCEPTION_H class MyException{ const char* err_msg; public: MyException(const char* const m):err_msg(m){} const char* const what(){return err_msg;} }; #endif Test.cpp #include "MyException.h" #include <iostream> using namespace std; void f(){ throw MyException("My Exception"); } int main(){ try{ f(); }catch(...) { cout<<"Nu se stie ce..."<<endl; } catch(MyException& e) {cout<<e.what()<<endl; } return 0; } Prinderea oricarui tip de exceptie Se va intra pe aici
Rearuncarea unei exceptii • Se doreste rearuncarea unei exceptii atunci cand exista niste resurse care trebuie eliberate (conexiuni in retea, memorie, etc) • Se foloseste clauza throw fara nici un argument in interiorul unui handler • De obicei, cand nu suntem interesati in cauza exceptiei, doar in a ne elibera resursele, folosim clauza catch(…) pentru a o prinde, a elibera resursele si apoi o aruncam mai departe
Rearuncarea unei exceptii void g(){} void f(){ char* s1; char* s2= new char[25]; try{ g(); }catch(...){delete [] s2; throw; } } int main(){ f(); return 0; }
#include <iostream> #include <fstream> using namespace std; void MyHandler() { ifstream f("in.txt"); int m; try { f>>m; cout<<"m = "<<m<<endl; throw "hello"; f>>m; cout<<"m = "<<m<<endl; } catch (const char*) { f.close(); cout <<"Exceptie prinsa in MyHandler\n"; throw; //rethrow char* out of function } } int main() { cout<< "Main start\n"; try { MyHandler(); } catch(const char*) { cout <<"Exceptie prinsa in Main\n"; } cout << "Main end"; return 0; } Rearuncarea unei exceptii 2 4 Main start m=2 Exceptie prinsa in MyHandler Exceptie prinsa in Main Main end
Exceptii neprinse • Daca nici un handler nu trateaza o exceptie, se va apela functia terminate(), care apeleaza la randul sau abort() – iesirea din program
Specificarea exceptiilor • Este posibila prezentarea multimii de exceptii pe care o functie le arunca direct sau indirect in cadrul declaratiei functiei • int getValue() throw (MyException*, char*, int); • O functie fara nici o specificare a exceptiilor poate arunca orice exceptie • int f(); • O functie cu specificarea exceptiilor vida nu poate arunca nici o exceptie • int f () throw(); • Daca o functie arunca o exceptie nespecificata in lista exceptiilor va genera apelul functiei unexpected() →terminate()
Distrugerea obiectelor • Sistemul de tratare a exceptiilor C++ garanteaza ca la parasirea domeniului de vizibilitate, toate obiectele create in acel domeniu de vizibilitate ai caror constructori s-au finalizat cu succes vor fi distruse prin apelarea automata a destructorilor
#ifndef T_H #define T_H #include <iostream> using std::cout; class Trace{ int objId; static int count; public: Trace(){objId=count++; cout<<"Object "<<objId<<" created"<<endl; if(count==3) throw 3; } ~Trace() {cout<<"Destroying "<<objId<<endl;} }; int Trace::count=0; #endif #include "Trace.h" int main(){ try{ Trace ob1; Trace array[5]; Trace n2; }catch(int i){cout<<"Caught "<<i<<endl;} return 0; } Distrugerea obiectelor Object 0 created Object 1 created Object 2 created Destroying 1 Destroying 0 Caught 3 Nu se finalizeaza niciodata
Exceptii standard • Derivate din clasa exception din <exception> • 2 clase principale, definite in <stdexcept>, care include <exception>: • logic_error (transmiterea unui argument invalid, etc) • runtime_error (probleme hard, probleme cu memoria) • Au cate un constructor cu argument de tip string (mesajul exceptiei) • Metoda what() care returneaza mesajul de exceptie
#ifndef MY_EXC_H #define MY_EXC_H #include <stdexcept> #include <iostream> using namespace std; class MyEx:public runtime_error{ public: MyEx(const string& msg=""):runtime_error(msg){} }; #endif #include "MyException.h" int main(){ try{ throw MyEx("mesaj "); }catch(MyEx& e){cout<<e.what()<<endl;} return 0; } Derivare din exceptii standard
Clase exceptie derivate • Derivate din logic_error • domain_error • invalid_argument • length_error • out_of_range • bad_cast • bad_typeid • Derivate din runtime_error • range_error • overflow_error • bad_alloc
#ifndef ET_H #define ET_H #include <stdexcept> using namespace std; class ExceptionTrace:public runtime_error{ public: ExceptionTrace( char* m):runtime_error(m){} ~ExceptionTrace(){} }; #endif #ifndef FVE_H #define FVE_H #include <string.h> #include "ExceptionTrace.h" class FullVectorException:public ExceptionTrace{ public: FullVectorException(char*m):ExceptionTrace(m){} ~FullVectorException(){} }; #endif #ifndef IOBE_H #define IOBE_H #include "ExceptionTrace.h" class IndexOutOfBoundsException:public ExceptionTrace{ public: IndexOutOfBoundsException(char*m):ExceptionTrace(m){} ~IndexOutOfBoundsException(){} }; #endif Vector cu exceptii
Vector.h #ifndef VECTOR_H #define VECTOR_H #include "FullVectorException.h" #include "IndexOutOfBoundsException.h" class Vector{ int* elem; int dim; int capac; public: Vector(int=10); Vector(const Vector&); void operator +=(int) throw(FullVectorException); int operator [](int) throw(IndexOutOfBoundsException); void realoc(); int vid() throw(); int dimensiune() throw(); ~Vector(); }; #endif
#include "Vector.h" #include <iostream> using namespace std; Vector::Vector(int d){ elem= new int[d]; dim=0; capac=d; } Vector::Vector(const Vector& v){ elem= new int[v.dim]; for (int i=0;i<v.dim;i++) elem[i]=v.elem[i]; dim=v.dim; } void Vector::operator +=(int x) throw(FullVectorException){ if(dim>=capac) throw FullVectorException("E plin vectorul"); elem[dim++]=x; } Vector::~Vector(){ delete [] elem; } int Vector::operator [](int i) throw(IndexOutOfBoundsException){ if (i>=dim) throw IndexOutOfBoundsException("Indexul e prea mare"); if (i<0) throw IndexOutOfBoundsException("Indexul e negativ"); return elem[i]; } void Vector::realoc(){ int* aux = elem; elem= new int[dim+dim/2]; for (int i=0;i<dim;i++) elem[i]=aux[i]; capac=dim+dim/2; delete []aux; cout<<"realoc"; } int Vector::vid() throw(){ return dim==0; } int Vector::dimensiune() throw(){ return dim; } Vector.cpp
#include "Vector.h" #include <iostream> using namespace std; int main(){ Vector v; try{ try{ for (int i=0;i<15;i++) v+=i; for(int i=2;;--i) cout<<v[i]<<"\n"; } catch(FullVectorException& e){ cout<<e.what()<<endl; v.realoc(); v+=11; throw; } catch(IndexOutOfBoundsException& e1){ cout<<e1.what()<<endl; throw; } catch(ExceptionTrace& e){ cout<<e.what()<<endl; } catch (...) {cout<< "altceva";} }catch(ExceptionTrace& e){ cout<<e.what()<<endl; } for(int i=0;i<v.dimensiune();i++) cout<<v[i]<<" "; return 0; } Test.cpp
Spatii de nume • Defineste un domeniu de vizibilitate in care nu pot fi folositi identificatori duplicati • Un spatiu de nume poate fi divizat la randul sau in mai multe spatii de nume • Crearea unui spatiu de nume – similar unei clase • Crearea de aliasuri de nume: • Namespace NumeLuuuuung{} • Namepace nume= NumeLuuuuung; namespace MyLib{ //declaratii } Fara “;”
Spatii de nume • Definitia unui spatiu de nume poate sa apara numai la nivel global, sau in interiorul altui domeniu de vizibilitate • Definitia unui spatiu de nume se poate realiza in mai multe fisiere #ifndef HEADER2_H #define HEADER2_H #include "Header1.h" namespace MyLib{ int y; void g(){} } #endif #ifndef HEADER1_H #define HEADER1_H namespace MyLib{ int x; void f(){} } #endif • #include "Header1.h" • #include "Header2.h" • int main(){ • MyLib::x=3; • MyLib::y=5; • MyLib::f(); • return 0; • }
Spatii de nume anonime namespace{ int i,j; } • Referirea la identificatori se face fara a folosi operatorul :: i=0; j=5;
Utilizarea spatiilor de nume • Utilizarea operatorului de rezolutie • Utilizarea directiveiusing pentru a introduce toate numele • Utilizarea declaratieiusing pentru a introduce cate un nume pe rand
namespaceX{ class Y{ static int i; public: void f(); }; class Z; void func(); } int X::Y::i=0; class X::Z{ int u,v; public: Z(int); … } X::Z::Z(int i){..} void X:: func(){} Utilizarea operatorului ::
Permite importarea unui spatiu de nume integral #ifndef NSINT_H #define NSINT_H #include <iostream> using namespace std; namespace Int{ class Integer{ int val; public: Integer(int =0); Integer(const Integer&); const Integer& operator+(); const Integer operator-(); const Integer operator+(const Integer&) const; const Integer operator-(const Integer&) const; Integer& operator=(const Integer&); bool operator==(const Integer&)const; bool operator!=(const Integer&)const; const Integer& operator++(); const Integer operator++(int); operator double(); operator int(); Integer& operator+=(const Integer&); char* toString(); friend ostream& operator<<(ostream& , Integer& ); friend istream& operator>>(istream& , Integer& ); }; } #endif Integer.cpp #include "NamespaceInt.h" using namespace Int; Integer::Integer(int x):val(x){} Integer::Integer(const Integer& x){ val=x.val; } Integer::operator double(){ return double(val); } Integer::operator int(){ return val; } const Integer& Integer::operator+(){ return *this; } const Integer Integer::operator-(){ // cout<<"aa"; //if (val<0) return Integer(-val); return Integer(-val); } const Integer Integer::operator+(const Integer& a) const{ return Integer(val+a.val); } … Directiva using
#ifndef NSM_H #define NSM_H #include "NamespaceInt.h" namespace Math{ using namespace Int; Integer a,b; } #endif #include "NamespaceMath.h" using namespace Math; int main(){ Integer a(6); Integer c(8); Integer sum=Math::a+c; cout<<sum.toString()<<endl; return 0; } Directiva using
Declaratia using • Permite injectarea numelor dintr-un spatiu de nume unul cate unul #ifndef UV_H #define UV_H #include <iostream> using namespace std; namespace U{ inline void f(){cout<<"U::f()"<<endl;} inline void g(){cout<<"U::g()"<<endl;} } namespace V{ inline void f(){cout<<"V::f()"<<endl;} inline void g(){cout<<"V::g()"<<endl;} } #endif #include "UV.h" int main(){ using namespace U; f(); using V::f; f(); g(); U::f(); return 0; } U::f() V::f() U::g() U::f()
Declaratia using #include <iostream> int main(){ using std::cout; using std::endl; cout<<“Hello"<<endl; return 0; }
Spatii de nume imbricate (nested) namespace A{ int i=5,j; inline f(){} namespace B{ int i=3; } } #include <iostream> using namespace std; #include "Namespace.h" int main(){ using namespace A; cout<<A::i<<endl; cout<<A::B::i<<endl; return 0; } 5 3
Intrari/Iesiri (I/O) • C: printf(…);scanf(…) • Neajunsuri: • Lucreaza doar cu cele 4 tipuri de date predefinte (char, int, float, double) si variatiile lor – nu este extensibila • Posibila solutie: • Pentru fiecare tip de data nou introdus sa se supraincarce functiile printf, scanf • DAR • Stringul de formatare???? • Solutia: • Utilizarea STREAMURILOR
Stream-uri • Definitie : un stream este un obiect care transporta si formateaza caractere de o lungime fixa (un flux de date de la o sursa (tastatura, fisier, zona de memorie) la o multime de destinatii (iesire: ecran, fisier, memorie) • Caracteristica esentiala a procesarii streamurilor – datele sunt transmise sau primite de la un stream unul cate unul (serial) • Tipuri de streamuri: • Streamuri de intrare (descendenti ai clasei istream) • Streamuri de iesire (descendenti ai clasei ostream) • Streamuri de intrare/iesire (descendenti ai clasei iostream)
Ierarhia de clase I/O ios Declara tot ce este comun tuturor streamurilor Defineste functii generice pentru intrare ostream istream Defineste functii generice pentru iesire iostream istream_withassign ostream_withassign iostream_withassign
Iostream • In biblioteca iostream – 2 operatori supraincarcati: • << (inserter) (folosit cu obiectul cout) • >>(extractor) (folosit cu obiectul cin) • Pentru a salva timp la compilare in proiectele mari, e bine sa nu se includa <iostream>, ci <iosfwd> • #include <iosfwd> • class Date{ • friend std::ostream& operator<<(std::ostream&, const Date&); • friend std::istream& operator>>(std::istream&, Date&); • };
Citirea de linii • In mod obisnuit cin>> se opreste la primul blank • Pentru a citi linii intregi – 3 posibilitati: • Functia membru get • Functia membru getline • 3 argumente: • Pointer la char – bufferul unde se va retine rezultatul • Dimensiunea bufferului • Caracterul de terminare (implicit ‘\n’) • Diferente: • get se opreste la primul caracter terminator, dar nu-l extrage • getline extrage caracterul terminator, dar nu il pune in buffer • Functia globala getline <string> • Citeste intr-un string
Get char* linie=new char[250]; cin.get(linie, 250); cout<<linie<<endl; while(strlen(linie)!=0){ cin.get(); cin.get(linie,250); cout<<linie<<endl; } cin.get(); Getline char* linie=new char[250]; cin.getline(linie, 250); cout<<linie<<endl; while(strlen(linie)!=0){ cin.getline(linie,250); cout<<linie<<endl; } Get/getline membre
Starea unui stream • 4 flaguri folosite pentru a testa starea unui stream • Valoarea unui flag poate fi testata apeland functiile: • good() – testeaza goodbit • eof() –testeaza eofbit+failbit • fail()- failbit sau badbit • bad() - badbit