Cerca nel sito:
ricerca
avanzata

Frasi Celebri...

L'unico dei tuoi bambini che non cresce e non se ne va e' tuo marito.

Anonimo 

Sondaggio:

Quale sistema operativo preferite?

Linux
Windows
DOS
MacOs
UNIX

visualizza risultati


 

Eccezioni e costruttori

 

Il meccanismo di stack unwinding (srotolamento dello stack) che si innesca quando viene sollevata una eccezione garantisce che gli oggetti allocati sullo stack vengano distrutti via via che il controllo esce dai vari blocchi applicativi.
Ma cosa succede se l'eccezione viene sollevata nel corso dell'esecuzione di un costruttore? In tal caso l'oggetto non puo` essere considerato completamente costruito ed il compilatore non esegue la chiamata al suo distruttore, viene comunque eseguita la chiamata dei distruttori per le componenti dell'oggetto che sono state create:


  #include < iostream >
  using namespace std;

  class Component {
    public:
      Component() {
        cout << "Component constructor called..." << endl;
      }
      ~Component() {
        cout << "Component distructor called..." << endl;
      }
  };

  class Composed {
    private:
      Component A;

    public:
      Composed() {
        cout << "Composed constructor called..." << endl;
        cout << "Throwing an exception..." << endl;
        throw 10;
      }
      ~Composed() {
        cout << "Composed distructor called..." << endl;
      }
  };

  int main() {
    try {
      Composed B;
    }
    catch (int) {
      cout << "Exception handled!" << endl;
    };
    return 0;
  }


Dall'output di questo programma:


  Component constructor called...
  Composed constructor called...
  Throwing an exception...
  Component distructor called...
  Exception handled!


e` possibile osservare che il distruttore per l'oggetto B istanza di Composed non viene eseguito perche` solo al termine del costruttore tale oggetto puo` essere considerato totalmente realizzato.

Le conseguenze di questo comportamento possono passare inosservate, ma e` importante tenere presente che eventuali risorse allocate nel corpo del costruttore non possono essere deallocate dal distruttore. Bisogna realizzare con cura il costruttore assicurandosi che risorse allocate prima dell'eccezione vengano opportunamente deallocate:


  #include < iostream >
  using namespace std;

  int Divide(int a, int b) throw(int) {
    if (b) return a/b;
    cout << endl;
    cout << "Divide: throwing an exception..." << endl;
    cout << endl;
    throw 10;
  }

  class Component {
    public:
      Component() {
        cout << "Component constructor called..." << endl;
      }
      ~Component() {
        cout << "Component distructor called..." << endl;
      }
  };

  class Composed {
    private:
      Component A;
      float* FloatArray;
      int AnInt;
    public:
      Composed() {
        cout << "Composed constructor called..." << endl;
        FloatArray = new float[10];
        try {
          AnInt = Divide(10,0);
        }
        catch(int) {
          cout << "Exception in Composed constructor...";
          cout << endl << "Cleaning up..." << endl;
          delete[] FloatArray;
          cout << "Rethrowing exception..." << endl;
          cout << endl;
          throw;
        }
      }
      ~Composed() {
        cout << "Composed distructor called..." << endl;
        delete[] FloatArray;
      }
  };

  int main() {
    try {
      Composed B;
    }
    catch (int) {
      cout << "main: exception handled!" << endl;
    };
    return 0;
  }


All'interno del costruttore di Composed viene sollevata una eccezione. Quando questo evento si verifica, il costruttore ha gia` allocato delle risorse (nel nostro caso della memoria); poiche` il distruttore non verrebbe eseguito e` necessario provvedere alla deallocazione di tale risorsa. Per raggiungere tale scopo, le operazioni soggette a potenziale fallimento vengono racchiuse in una try seguita dall'opportuna catch. Nel exception handler tale risorsa viene deallocata e l'eccezione viene nuovamente propagata per consentire alla main di intraprendere ulteriori azioni.
Ecco l'output del programma:


  Component constructor called...
  Composed constructor called...

  Divide: throwing an exception...

  Exception in Composed constructor...
  Cleaning up...
  Rethrowing exception...

  Component distructor called...
  main: exception handled!


Si noti che se la catch del costruttore della classe Composed non avesse rilanciato l'eccezione, il compilatore considerando gestita l'eccezione, avrebbe terminato l'esecuzione del costruttore considerando B completamente costruito. Cio` avrebbe comportato la chiamata del distruttore al termine dell'esecuzione della main con il conseguente errore dovuto al tentativo di rilasciare nuovamente la memoria allocata per FloatArray.
Per verificare cio` si modifichi il programma nel seguente modo:


  #include < iostream >
  using namespace std;

  int Divide(int a, int b) throw(int) {
    if (b) return a/b;
    cout << endl;
    cout << "Divide: throwing an exception..." << endl;
    cout << endl;
    throw 10;
  }

  class Component {
    public:
      Component() {
        cout << "Component constructor called..." << endl;
      }
      ~Component() {
        cout << "Component distructor called..." << endl;
      }
  };

  class Composed {
    private:
      Component A;
      float* FloatArray;
      int AnInt;
    public:
      Composed() {
        cout << "Composed constructor called..." << endl;
        FloatArray = new float[10];
        try {
          AnInt = Divide(10,0);
        }
        catch(int) {
          cout << "Exception in Composed constructor...";
          cout << endl << "Cleaning up..." << endl;
          delete[] FloatArray;
        }
      }
      ~Composed() {
        cout << "Composed distructor called..." << endl;
      }
  };

  int main() {
    try {
      Composed B;
      cout << endl << "main: no exception here!" << endl;
    }
    catch (int) {
      cout << endl << "main: Exception handled!" << endl;
    };
  }


eseguendolo otterrete il seguente output:


  Component constructor called...
  Composed constructor called...

  Divide: throwing an exception...

  Exception in Composed constructor...
  Cleaning up...

  main: no exception here!
  Composed distructor called...
  Component distructor called...


Come si potra` osservare, il blocco try della main viene eseguito normalmente e l'oggetto B viene distrutto non in seguito all'eccezione, ma solo perche` si esce dallo scope del blocco try cui appartiene.

La realizzazione di un costruttore nella cui esecuzione puo` verificarsi una eccezione, e` dunque un compito non banale e in generale sono richieste due operazioni:

  1. Eseguire eventuali pulizie all'interno del costruttore se non si e` in grado di terminare correttamente la costruzione dell'oggetto;
  2. Se il distruttore non termina correttamente (ovvero l'oggetto non viene totalmente costruito), propagare una eccezione anche al codice che ha invocato il costruttore e che altrimenti rischierebbe di utilizzare un oggetto non correttamente creato.

 

 

successivo
–«  INDICE  »–

 

 

 

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