<<Pagina Materiale

 
 
Exceptii si tratarea lor 


Aspecte teoretice

In scrierea programelor trebuie sa se specifice tratarea erorilor ce pot apare in cazul functionarii. Puteti citi despre cazuri la care erorile aparute in timpul exploatarii au cauzat pierderi considerabile la Software Horror Stories.

In acest material vom examina:

  • ce sunt exceptiile
  • cum sunt tratate.
Notiunea de exceptie

Erorile apar datorita greselilor de programare, logica; exceptiile apar datorita unor probleme aparute in timpul rularii ( depasire de memorie, date in afara limitelor prescrise ).

Variantele de tratare a exceptiilor ar fi:

  • Abandonarea programului
  • Informarea utilizatorului si terminarea programului
  • Informarea utilizatorului, astfel incat acesta sa ia masuri pentru continuarea programului
  • Actiuni automate de corectare, fara a informa utilizatorul.
C++ ofera o metoda integrata si sigura pentru tratarea exceptiilor predictibile ce pot apare in timpul rularii programelor.

O exceptie este un obiect care este pasat din zona codului unde a aparut o eroare, secventei de tratare a problemei, functie de  tipul exceptiei - transmiterea poate fi facuta prin valoare sau referinta.

Mecanismul de tratare a exceptiilor

Blocuri try sunt create pentru a include codul unde pot apare probleme:

try
{
  // cod sau apel functie 
  .....
  FunctieProbleme();
  .....
}

Semnalarea exceptiilor in blocurile try se face prin instructiunea throw,  care trebuie executata in interiorul blocului try sau intr-o functie apelata din bloc;

throw exceptie;

Blocuri catch trateaza exceptiile semnalate de blocurile try, urmandu-le pe acestea - la semnalarea unei exceptii intr-un bloc try se trece la blocul catch ce-l urmeaza; daca pentru o exceptie lansata nu exista un bloc catch aplicabil, poate sa apara o terminare anormala a programului. 

try
{
  FunctieProbleme();
}
catch(OutOfMemory)
{
  // actiuni specifice
}
catch(FileNotFound)
{
  // alte actiuni specifice
}

Un bloc catch poate avea si forma:

catch(...)
{
  // tratare exceptie de orice tip
}

ce apare in general dupa celelalte, tratand exceptiile ce nu au fost specificate de acestea.

Pasii pentru tratarea exceptiilor sunt:

  • identificarea zonelor de cod unde ar putea apare exceptii si includerea lor in blocuri try
  • crearea de blocuri catch pentru tratarea exceptiilor semnalate.
Relansarea unei exceptii

O exceptie poate fi relansata, dintr-un catch, prin throw.

Aceasta face ca exceptia curenta sa fie retransmisa unei secvente try/catch exterioare. Motivul retransmiterii este sa se permita mai multor manipulatori catch, accesul la aceleasi exceptii. La relansare, o exceptie nu va fi preluata de aceeasi instructiune catch, ci se va deplasa spre urmatoarea. A se vedea exemplul 3.

Sus
Exemple

1.Iata un prim exemplu simplu care semnaleaza o exceptie de tip intreg. La executie sa se observe faptul ca dupa semnalarea erorii prin throw, instructiile care urmeaza in blocul try nu se mai executa, controlul fiind predat blocului catch.

Iesirea programului va fi:

Intrare in main
Intrare in blocul try
Exceptia tratata este:10
Terminare main
 

#include <iostream.h>

int main(){

  cout<<"Intrare in main"<<endl;

  try{
    cout<<"Intrare in blocul try"<<endl;
    throw 10; //lansare exceptie, se preda controlul blocului catch
    cout<<"Instructiune dupa throw"<<endl; //nu se va tipari
  } //terminare bloc try
  catch (int e){ //linie marcata
    cout<<"Exceptia tratata este:"<<e<<endl; 
  }
  cout<<"Terminare main"<<endl;
  return 0;
}

Daca linia marcata se inlocuieste cu:

catch (float e){

programul se va termina anormal, din cauza ca exceptia de tip intreg semnalata in try, nu gaseste un bloc catch pentru tratare.

2.Programul de mai sus se modifica, astfel incat semnalarea exceptiei se face intr-o functie apelata in blocul try:

Iesirea programului va fi:

Intrare in main
Intrare in blocul try
Intrare in functie cu valoarea:-4
Exceptia tratata este:-4
Intrare in functie cu valoarea:0
In functie dupa throw
Intrare in functie cu valoarea:10
Exceptia tratata este:10
Terminare try
Terminare main
 

#include <iostream.h>

void functie(int i){
  cout<<"Intrare in functie cu valoarea:"<<i<<endl;
  if(i)throw i;
  cout<<"In functie dupa throw"<<endl; //nu se va tipari daca se semnaleaza exceptie
}

int main(){

  cout<<"Intrare in main"<<endl;

  try{
    cout<<"Intrare in blocul try"<<endl;
    functie(-4);
    functie(0);
    functie(10);
    cout<<"Terminare try"<<endl; 
  } //terminare bloc try
  catch (int e){ 
    cout<<"Exceptia tratata este:"<<e<<endl; 
  }
  cout<<"Terminare main"<<endl;
  return 0;
}

3.In programul de mai jos se relanseaza o exceptie de tip char*. 

Iesirea programului va fi:

Intrare in main
Intrare in blocul try din main
Intrare in blocul try din functie
Tratare exceptie curs C/C++ din catch interior
Intrare in blocul try din functie
Tratare exceptie 2002 din catch interior
Exceptia tratata este:2002
Terminare main

Sa se elimine if din linia de relansare si sa se studieze functionarea programului in acest caz; dar daca se comenteze linia?
 

#include <iostream.h>
#include <string.h>

void functie(char *s){
  try{
    cout<<"Intrare in try din functie"<<endl;
    throw s;
  }
  catch{
    cout<<"Tratare exceptie "<<s<<" din catch interior"<<endl;
    if(strlen(s)<=4)throw; // se relanseaza exceptia daca sirul are lungimea <=4 // linie marcata 
  }
}

int main(){

  cout<<"Intrare in main"<<endl;

  try{
    cout<<"Intrare in blocul try din main"<<endl;
    functie("curs C/C++");
    functie("2002");
    functie("ianuarie"); //nu se va apela
    cout<<"Terminare try"<<endl; //nu se ajunge aici
  } //terminare bloc try
  catch (char *sir){ 
    cout<<"Exceptia tratata este:"<<sir<<endl; 
  }
  cout<<"Terminare main"<<endl;
  return 0;
}

4.In programul de mai jos se defineste clasa Array; se semnaleaza exceptii, obiecte ale clasei vide xBoundary daca indicii sunt in afara domeniului. 

De fapt, pentru clasa xBoundary, compilatorul creaza implicit constructor, destructor, copy constructor, copy operator.

A se observa definirea constructorului de copiere, a operatorului de copiere =, dubla supraincarcare pentru operatorul de indexare [] - pentru cazul cand se citeste sau se modifica valoarea unui element de tablou si supraincarea operatorului de iesire << ( declarat ca si functie friend ).

La rulare se va observa ca dupa initializarea elementelor tabloului ( indicii intre 0 si 19 ), pentru indicele 20 se semnaleaza exceptie, tratata in catch, dupa care se iese din main.

Se poate modifica programul astfel incat el sa semnaleze exceptie pentru toti indicii intre 20 si 29?
 

#include <iostream.h>

const int DefaultSize = 10;

class Array
{
  private:
       int *pType; //adresa tabloului
       int  itsSize;  //numar elemente
  public:
    // constructori
      Array(int itsSize = DefaultSize);
      Array(const Array &An_Array);  //tabloul se construieste ca fiind copia celui parametru 
       ~Array() { delete [] pType;}

    // operatori
      Array& operator=(const Array&);
      int& operator[](int offSet);
      const int& operator[](int offSet) const;

    // metode
      int GetitsSize() const { return itsSize; }

    // functie friend 
      friend ostream& operator<< (ostream&, const Array&);

    // se defineste clasa xBoundary ( vida ) -obiectele ei vor fi exceptii
      class xBoundary {}; 
};

Array::Array(int size):itsSize(size){
      pType = new int[size];
      for (int i = 0; i<size; i++)
        pType[i] = 0; //se initializeaza elementele cu 0
}

Array& Array::operator=(const Array &An_Array){ 
     if (this == &An_Array)
        return *this;
     delete [] pType;
     itsSize = An_Array.GetitsSize();
     pType = new int[itsSize];
     for (int i = 0; i<itsSize; i++)
       pType[i] = An_Array[i];
     return *this;
}

Array::Array(const Array &An_Array){
     itsSize = An_Array.GetitsSize();
     pType = new int[itsSize];
     for (int i = 0; i<itsSize; i++)
        pType[i] = An_Array[i];
}

int& Array::operator[](int offSet){
     int size = GetitsSize();
     if (offSet >= 0 && offSet < GetitsSize())
        return pType[offSet];
     throw xBoundary();
     return pType[0]; // nu se ajunge aici, se face return doar pentru a nu apare eroare in compilare 
}

const int& Array::operator[](int offSet) const{
     int mysize = GetitsSize();
     if (offSet >= 0 && offSet < GetitsSize())
        return pType[offSet];
     throw xBoundary();
     return pType[0]; // nu se ajunge aici, se face return doar pentru a nu apare eroare in compilare 
}

ostream& operator<< (ostream& output, const Array& theArray){
     for (int i = 0; i<theArray.GetitsSize(); i++)
        output << "[" << i << "] " << theArray[i] << endl;
     return output;
}

int main(){
     Array intArray(20);
     try{
        for (int j = 0; j< 30; j++){
           intArray[j] = j;
           cout << "intArray[" << j << "] initializat" << endl;
        }
     }
     catch (Array::xBoundary){
        cout << "Indice eronat!\n";
     }
     cout << "Terminare main\n";
     return 0;
}

Sus
Exercitii

1.Scrieti un program care citeste repetat intregi pana la introducerea valorii 0. Intregii se vor citi ca siruri de caractere, semnalandu-se exceptii la valori in afara domeniului int si la tastarea caracterelor invalide.

2.Se cere sa se defineasca o clasa Queue care modeleaza functionarea unei cozi ( lista FIFO ) cu elemente de tip char. Dimensiunea cozii este limitata si se specifica la creare - se semnaleaza exceptie in constructor daca dimensiunea este <=0. Operatiile care se pot aplica asupra cozii sunt:

  • void Put(char) - pentru introducerea caracterului parametru - se semnaleaza exceptie cand coada e plina
  • char Get() - pentru extragerea caracterului de la inceputul cozii - se semnaleaza exceptie cand coada e goala.
Sus
<<Pagina Materiale



Copyright © 2001-2002. Carmen Holotescu
All rights reserved. Published by Timsoft