1 / 68

Tratarea Exceptiilor.Spatii de nume. I/O

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.

thor
Download Presentation

Tratarea Exceptiilor.Spatii de nume. I/O

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. Tratarea Exceptiilor.Spatii de nume. I/O Curs 12-13 

  2. 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

  3. 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++)

  4. 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

  5. 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

  6. 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

  7. 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.

  8. 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

  9. 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)

  10. Aruncarea unei exceptii • Se poate arunca orice tip de data (inclusiv tipuri de date predefinite), dar de obicei se creeaza clase speciale

  11. 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) }

  12. 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}

  13. 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

  14. 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

  15. 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

  16. 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

  17. 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

  18. 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

  19. 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

  20. 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

  21. 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; }

  22. #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

  23. Exceptii neprinse • Daca nici un handler nu trateaza o exceptie, se va apela functia terminate(), care apeleaza la randul sau abort() – iesirea din program

  24. 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()

  25. 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

  26. #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

  27. 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

  28. #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

  29. 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

  30. #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

  31. 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

  32. #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

  33. #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

  34. 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 “;”

  35. 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; • }

  36. Spatii de nume anonime namespace{ int i,j; } • Referirea la identificatori se face fara a folosi operatorul :: i=0; j=5;

  37. 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

  38. 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 ::

  39. 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

  40. #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

  41. 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()

  42. Declaratia using #include <iostream> int main(){ using std::cout; using std::endl; cout<<“Hello"<<endl; return 0; }

  43. 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

  44. 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

  45. 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)

  46. 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

  47. 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&); • };

  48. 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

  49. 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

  50. 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

More Related