Cerca nel sito:
ricerca
avanzata

Frasi Celebri...

I tuoi sogni sono troppo piccoli.

Anonimo 

Sondaggio:

Quale browser utilizzate?

Internet Explorer
Mozilla/Netscape
Opera
Lynx
Konqueror
Altro

visualizza risultati


 

Costruttori per oggetti composti

L'inizializzazione di un ogggetto composto richiede che siano inizializzate tutte le sue componenti. Abbiamo visto che un attributo non puo` essere inizializzato mentre lo si dichiara (infatti gli attributi static vanno inizializzati fuori dalla dichiarazione di classe (vedi capitolo VIII, paragrafo 6); la stessa cosa vale per gli attributi di tipo oggetto:


  class Composed {
    public:
      /* ... */

    private:
      unsigned int Attr = 5;    // Errore!
      Component Elem(10, 5);    // Errore!
      /* ... */
  };


Il motivo e` ovvio, eseguendo l'inizializzazione nel modo appena mostrato il programmatore sarebbe costretto ad inizializzare la componente sempre nello stesso modo; nel caso si desiderasse una inizializzazione alternativa, saremmo costretti a eseguire altre operazioni (e avremmo aggiunto overhead inutile).
La creazione di un oggetto che contiene istanze di altre classi richiede che vengano prima chiamati i costruttori per le componenti e poi quello per l'oggetto stesso; analogamente ma in senso contrario, quando l'oggetto viene distrutto, viene prima chiamato il distruttore per l'oggetto composto, e poi vengono eseguiti i distruttori per le singole componenti.
Il processo puo` sembrare molto complesso, ma fortunatamente e` il compilatore che si occupa di tutta la faccenda, il programmatore deve occuparsi solo dell'oggetto con cui lavora, non delle sue componenti. Al piu` puo` capitare che si voglia avere il controllo sui costruttori da utilizzare per le componenti; l'operazione puo` essere eseguita utilizzando la lista di inizializzazione, come mostra l'esempio seguente:


  #include < iostream >
  using namespace std;

  class SmallObj {
    public:
      SmallObj() {
        cout << "Costruttore SmallObj()" << endl;
      }
      SmallObj(int a, int b) : A1(a), A2(b) {
        cout << "Costruttore SmallObj(int, int)" << endl;
      }
      ~SmallObj() {
         cout << "Distruttore ~SmallObj()" << endl;
      }

    private:
      int A1, A2;
  };

  class BigObj {
    public:
      BigObj() {
        cout << "Costruttore BigObj()" << endl;
      }
      BigObj(char c, int a = 0, int b = 1)
       : Obj(a, b), B(c) {
        cout << "Costruttore BigObj(char, int, int)"
             << endl;
      }
      ~BigObj() {
         cout << "Distruttore ~BigObj()" << endl;
      }

    private:
      SmallObj Obj;
      char B;
  };

  int main(int, char* []) {
    BigObj Test(15);
    BigObj Test2;
    return 0;
  }


il cui output sarebbe:


  Costruttore SmallObj(int, int)
  Costruttore BigObj(char, int, int)
  Costruttore SmallObj()
  Costruttore BigObj()
  Distruttore ~BigObj()
  Distruttore ~SmallObj()
  Distruttore ~BigObj()
  Distruttore ~SmallObj()


L'inizializzazione della variabile Test2 viene eseguita tramite il costruttore di default, e poiche` questo non chiama esplicitamente un costruttore per la componente SmallObj automaticamente il compilatore aggiunge una chiamata a SmallObj::SmallObj(); nel caso in cui invece desiderassimo utilizzare un particolare costruttore per SmallObj bisogna chiamarlo esplicitamente come fatto in BigObj::BigObj(char, int, int) (utilizzato per inizializzare Test).

Si poteva pensare di realizzare il costruttore nel seguente modo:


  BigObj::BigObj(char c, int a = 0, int b = 1) {
    Obj = SmallObj(a, b);
    B = c;
    cout << "Costruttore BigObj(char, int, int)" << endl;
  }


ma benche` funzionalmente equivalente al precedente, non genera lo stesso codice. Infatti poiche` un costruttore per SmallObj non e` esplicitamente chiamato nella lista di inizializzazione e poiche` per costruire un oggetto complesso bisogna prima costruire le sue componente, il compilatore esegue una chiamata a SmallObj::SmallObj() e poi passa il controllo a BigObj::BigObj(char, int, int). Conseguenza di cio` e` un maggiore overhead dovuto a due chiamate di funzione in piu`: una per SmallObj::SmallObj() (aggiunta dal compilatore) e l'altra per SmallObj::operator=(SmallObj&) (dovuta alla prima istruzione del costruttore).
Il motivo di un tale comportamento potrebbe sembrare piuttosto arbitrario, tuttavia in realta` una tale scelta e` dovuta alla necessita` di garantire sempre che un oggetto sia inizializzato prima di essere utilizzato.
Ovviamente poiche` ogni classe possiede un solo distruttore, in questo caso non esistono problemi di scelta!

In pratica possiamo riassumere quanto detto dicendo che:

  1. la costruzione di un oggetto composto richiede prima la costruzione delle sue componenti, utilizzando le eventuali specifiche presenti nella lista di inizializzazione del suo costruttore; in caso non venga specificato il costruttore da utilizzare per una componente, il compilatore utilizza quello di default. Alla fine viene eseguito il corpo del costruttore per l'oggetto composto;
  2. la distruzione di un oggetto composto avviene eseguendo prima il suo distruttore e poi il distruttore di ciascuna delle sue componenti;

In quanto detto e` sottointeso che se una componete di un oggetto e` a sua volta un oggetto composto, il procedimento viene iterato fino a che non si giunge a componenti di tipo primitivo.

Ora che e` noto il meccanismo che regola l'inizializzazione di un oggetto composto, resta chiarire come vengono esattamente generati il costruttore di default e quello di copia.
Sappiamo che il compilatore genera automaticamente un costruttore di default se il programmatore non ne definisce uno qualsiasi, in questo caso il costruttore di default fornito automaticamente, come e` facile immaginare, non fa altro che chiamare i costruttori di default delle singole componenti, generando un errore se per qualche componente non esiste un tale costruttore. Analogamente il costruttore di copia che il compilatore genera (solo se il programmatore non lo definisce esplicitamente) non fa altro che richiamare i costruttori di copia delle singole componenti.

 

successivo
–«  INDICE  »–

 

 

 

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