Cerca nel sito:
ricerca
avanzata

Frasi Celebri...

I veri ingegneri del software lavorano dalle 9:00 alle 17:00, perche' cosi' e' descritto il loro lavoro nelle specifiche formali. Lavorare piu' tardi li farebbe sentire come se usassero una procedura esterna non documentata.

Anonimo 

Sondaggio:

La Ferrari ?...

Una leggenda
La storia
Schumacher
L'auto per eccellenza
Un sogno

visualizza risultati


 

Gestire le eccezioni

 

Quanto abbiamo visto chiaramente non e` sufficiente, non basta poter sollevare (segnalare) una eccezione ma e` necessario poterla anche catturare e gestire.
L'intenzione di catturare e gestire l'eventuale eccezione deve essere segnalata al compilatore utilizzando un blocco try:


  #include < iostream >
  using namespace std;

  int Divide(int a, int b) throw(char*) {
    if (b) return a/b;
    throw "Errore";
  }

  int main() {
    cout << "Immettere il dividendo: ";
    int a;
    cin >> a;
    cout << endl << "Immettere il divisore: ";
    int b;
    cin >> b;
    try {
      cout << Divide(a, b);
    }
    /* ... */
  }


Utilizzando try e racchidendo tra parentesi graffe (le parentesi si devono utilizzate sempre) il codice che puo` generare una eccezione si segnala al compilatore che siamo pronti a gestire l'eventuale eccezione.
Ci si potra` chiedere per quale motivo sia necessario informare il compilatore dell'intenzione di catturare e gestire l'eccezione, il motivo sara` chiaro in seguito, al momento e` sufficiente sapere che cio` ha il compito di indicare quando certi automatismi dovranno arrestarsi e lasciare il controllo a codice ad hoc preposto alle azioni del caso.
Il codice in questione dovra` essere racchiuso all'interno di un blocco catch che deve seguire il blocco try:


  #include < iostream >
  using namespace std;

  int Divide(int a, int b) throw(char*) {
    if (b) return a/b;
    throw "Errore, divisione per 0";
  }

  int main() {
    cout << "Immettere il dividendo: ";
    int a;
    cin >> a;
    cout << endl << "Immettere il divisore: ";
    int b;
    cin >> b;
    cout << endl;
    try {
      cout << "Il risultato e` " << Divide(a, b);
    }
    catch(char* String) {
      cout << String << endl;
      return -1;
    }
    return 0;
  }


Il generico blocco catch potra` gestire in generale solo una categoria di eccezioni o una eccezione generica. Per fornire codice diverso per diverse tipologie di errori bisognera` utilizzare piu` blocchi catch:


   try {
      /* ... */
   }
   catch(Type1 Id1) {
     /* ... */
   }
   catch(Type2 Id2) {
     /* ... */
   }

   /* Altre catch */

   catch(TypeN IdN) {
     /* ... */
   }

   /* Altro */


Ciascuna catch e` detta exception handler e riceve un parametro che e` il tipo di eccezione che viene gestito in quel blocco. Nel caso generale un blocco try sara` seguito da piu` blocchi catch, uno per ogni tipo di eccezione possibile all'interno di quel try. Si noti che le catch devono seguire immediatamente il blocco try.

Quando viene generata una eccezione (throw) il controllo risale indietro fino al primo blocco try. Gli oggetti staticamente allocati (che cioe` sono memorizzati sullo stack) fino a quel momento nei blocchi da cui si esce vengono distrutti invocando il loro distruttore (se esiste). Nel momento in cui si giunge ad un blocco try anche gli oggetti staticamente allocati fino a quel momento dentro il blocco try vengono distrutti ed il controllo passa immediatamente dopo la fine del blocco.
Il tipo dell'oggetto creato con throw viene quindi confrontato con i parametri delle catch che seguono la try. Se viene trovata una catch del tipo corretto, si passa ad eseguire le istruzioni contenute in quel blocco, dopo aver inizializzato il parametro della catch con l'oggetto restituito con throw. Nel momento in cui si entra in un blocco catch, l'eccezione viene considerata gestita ed alla fine del blocco catch il controllo passa alla prima istruzione che segue la lista di catch (sopra indicato con "/* Altro */").
Vediamo un esempio:


  #include < iostream >
  #include < string.h >
  using namespace std;

  class Test {
      char Name[20];
    public:
      Test(char* name){
        Name[0] = '\0';
        strcpy(Name, name);
        cout << "Test constructor inside "
             << Name << endl;
      }
      ~Test() {
        cout << "Test distructor inside "
             << Name << endl;
      }
  };

  int Sub(int b) throw(int) {
    cout << "Sub called" << endl;
    Test k("Sub");
    Test* K2 = new Test("Sub2");
    if (b > 2) return b-2;
    cout << "exception inside Sub..." << endl;
    throw 1;
  }

  int Div(int a, int b) throw(int) {
    cout << "Div called" << endl;
    Test h("Div");
    b = Sub(b);
    Test h2("Div 2");
    if (b) return a/b;
    cout << "exception inside Div..." << endl;
    throw 0;
  }

  int main() {
    try {
      Test g("try");
      int c = Div(10, 2);
      cout << "c = " << c << endl;
    }       // Il controllo ritorna qua
    catch(int exc) {
      cout << "exception catched" << endl;
      cout << "exception value is " << exc << endl;
    }
    return 0;
  }


La chiamata a Div all'interno della main provoca una eccezione nella Sub, viene quindi distrutto l'oggetto k ed il puntatore k2, ma non l'oggetto puntato (allocato dinamicamente). La deallocazione di oggetti allocati nello heap e` a carico del programmatore.
In seguito alla eccezione, il controllo risale a Div, ma la chiamata a Sub non era racchiusa dentro un blocco try e quindi anche Div viene terminata distruggendo l'oggetto h. L'oggetto h2 non e` stato ancora creato e quindi nessun distruttore per esso viene invocato.
Il controllo e` ora giunto al blocco che ha chiamato la Div, essendo questo un blocco try, vengono distrutti gli oggetti g e c ed il controllo passa nel punto in cui si trova il commento.
A questo punto viene eseguita la catch poiche` il tipo dell'eccezione e` lo stesso del suo argomento e quindi il controllo passa alla return della main.
Ecco l'output del programma:


  Test constructor inside try
  Div called
  Test constructor inside Div
  Sub called
  Test constructor inside Sub
  Test constructor inside Sub 2
  exception inside Sub...
  Test distructor inside Sub
  Test distructor inside Div
  Test distructor inside try
  exception catched
  exception value is 0


Si provi a tracciare l'esecuzione del programma e a ricostruirne la storia, il meccanismo diverra` abbastanza chiaro.

Il compito delle istruzioni contenute nel blocco catch costituiscono quella parte di azioni di recupero che il programma deve svolgere in caso di errore, cosa esattamente mettere in questo blocco e` ovviamente legato alla natura del programma e a cio` che si desidera fare; ad esempio ci potrebbero essere le operazioni per eseguire dell'output su un file di log. E` buona norma studiare gli exception handler in modo che al loro interno non possano verificarsi eccezioni.

Nei casi in cui non interessa distinguere tra piu` tipologie di eccezioni, e` possibile utilizzare un unico blocco catch utilizzando le ellissi:


  try {
    /* ... */
  }
  catch(...) {
    /* ... */
  }


In altri casi invece potrebbe essere necessari passare l'eccezione ad un blocco try ancora piu` esterno, ad esempio perche` a quel livello e` sufficiente (o possibile) fare solo certe operazioni, in questo caso basta utilizzare throw all'interno del blocco catch per reinnescare il meccanismo delle eccezioni a partire da quel punto:


  try {
    /* ... */
  }
  catch(Type Id) {
    /* ... */
    throw;    // Bisogna scrivere solo throw
  }


In questo modo si puo` portare a conoscenza dei blocchi piu` esterni della condizione di errore.

 

successivo
–«  INDICE  »–

 

 

 

 
Powered by paper&pencil (carta&matita ) - Copyright © 2001-2022 Cataldo Sasso