Costruttori e distruttori degli oggetti

 


 

Costruzione e distruzione di un oggetto

 

Abbiamo detto più volte che quando un oggetto, istanza di un tipo nativo o astratto, viene creato, si dice che quell'oggetto è costruito. Analogamente, quando l'oggetto cessa di esistere, si dice che quell'oggetto è distrutto.

Vediamo le varie circostanze in cui un oggetto può essere costruito o distrutto:

  1. Un oggetto automatico (cioè locale non statico) viene costruito ogni volta che la sua definizione viene incontrata durante l'esecuzione del programma, e distrutto ogni volta che il programma esce dall'ambito in cui tale definizione si trova.

  2. Un oggetto locale statico viene costruito la prima volta che la sua definizione viene incontrata durante l'esecuzione del programma, e distrutto una sola volta, quando il programma termina.

  3. Un oggetto allocato nella memoria dinamica (area heap ) viene  costruito mediante l'operatore new e distrutto mediante l'operatore delete.

  4. Un oggetto, membro  non statico di  una classe, viene costruito ogni volta che (o meglio, immediatamente prima che) viene costruito un oggetto della classe di cui è membro, e distrutto ogni volta che (o meglio, immediatamente dopo che) lo stesso oggetto viene distrutto.

  5. Un oggetto, elemento di un array, viene costruito o distrutto ogni volta che l'array di cui fa parte viene costruito o distrutto.

  6. Un oggetto globale, un oggetto di un namespace o un membro statico di  una classe, viene costruito una sola volta, alla "partenza" del programma e distrutto quando il programma termina.

  7. Infine, un oggetto temporaneo viene costruito per memorizzare risultati parziali durante la valutazione di un'espressione, e distrutto alla fine dell'espressione completa in cui compare.

Come si può notare, la costruzione o distruzione di un oggetto può avvenire in momenti diversi, in base alla categoria dell'oggetto che si sta considerando. In ogni caso, sia durante la costruzione che durante la distruzione, potrebbero rendersi necessarie delle operazioni specifiche. Per esempio, se un membro di  una classe è un puntatore, potrebbe essere necessario creare l'area puntata (che non viene fatto automaticamente, come nel caso degli array) e allocarla dinamicamente con l'operatore new; quest'area dovrà però essere rilasciata, prima e poi (con l'operatore delete), e capita non di rado che non lo si possa fare prima della distruzione dell'oggetto. Poichè d'altra parte un oggetto può anche essere costruito o distrutto automaticamente, si pone il problema di come "intercettare" il momento della sua  costruzione o distruzione.

Nel caso che gli oggetti siano istanze di una classe,  il C++ mette a disposizione un mezzo molto potente, che consiste nella possibilità di definire dei particolari metodi della classe, che il programma riconosce come funzioni da eseguire al momento della costruzione o distruzione di un oggetto. Questi metodi prendono il nome di costruttori e distruttori degli oggetti. Il loro scopo principale è, per i costruttori, di inizializzare i membri e/o allocare risorse, per i distruttori, di rilasciare le risorse allocate.

 


 

Costruttori

 

I costruttori degli oggetti devono sottostare alle seguenti regole (ci rifaremo al solito esempio della classe point):

  1. devono avere lo stesso nome della classe
              prototipo: point(......);
    definizione esterna:      point::point(......)  {......}

  2. non bisogna specificare il tipo di ritorno (neanche void)
    NOTA: in realtà la chiamata di un costruttore può anche essere inserita in un'espressione; ciò significa che un costruttore ritorna "qualcosa" e precisamente .... l'oggetto che ha appena creato!

  3. ammettono argomenti e defaults; i costruttori senza argomenti (o con tutti argomenti di default) sono detti: "costruttori di default"
    prototipo di costruttore di default della classe point:         point( );
    prototipo di costruttore della classe point con un argomento required e uno di default:
               point(double,double=0.0);

  4. possono esistere più costruttori, in overload, in una stessa classe. Il C++ li distingue in base alla lista degli argomenti. Come tutte le funzioni in overload, non sono ammessi costruttori che differiscano solo per gli argomenti di default.

  5. devono essere dichiarati come funzioni-membro pubbliche, in quanto sono sempre chiamati dall'esterno della classe a cui appartengono.

I costruttori non sono obbligatori: se una classe non ne possiede, il C++ fornisce un costruttore di default con "corpo nullo" .  

Il costruttore di default (dichiarato nella classe oppure fornito dal C++) viene eseguito automaticamente nel momento in cui l'oggetto viene creato nel programma (si vedano i vari casi elencati nella sezione precedente). Esempio :
    definizione del costruttore di default di point:   point::point( )  {x=3.5; y=2.1;}
definizione dell'oggetto p, istanza di point: point p ;

nel momento in cui è l'eseguita l'istruzione di definizione dell'oggetto p, il costruttore di default va in esecuzione automaticamente, inizializzando p con 3.5 nel membro x e 2.1 nel membro y.

Se invece in una classe esiste almeno un costruttore con argomenti, il C++ non mette a disposizione alcun costruttore di default e perciò questo, se necessario, va esplicitamente definito come metodo della classe. In sua assenza, i costruttori con argomenti non vengono invocati automaticamente e pertanto ogni istruzione del programma che determini, direttamente o indirettamente, la creazione di un oggetto, deve contenere la chiamata esplicita di uno dei costruttori disponibili, nel modo che dipende dalla categoria dell'oggetto interessato. Esamineremo i vari casi separatamente, rifacendoci all'elenco illustrato nella sezione precedente.

Per il momento consideriamo il caso più frequente, che è quello di un oggetto singolo creato direttamente mediante la definizione del suo nome (casi 1., 2. e 6.): i modi possibili per invocare un costruttore con argomenti sono due, come è  mostrato dal seguente esempio:

      definizione del costruttore di point :   point::point(double x0, double y0)
{x=x0; y=y0;}
definizione dell'oggetto p, istanza di point :
                                  prima forma : point p (3.5, 2.1);
                                 seconda forma : point p = point(3.5, 2.1);

la prima forma è più concisa, ma la seconda è più chiara, in quanto ha proprio l'aspetto di una inizializzazione tramite chiamata esplicita di una funzione. In entrambi i casi viene invocato un costruttore con due argomenti di tipo double, che inizializza p inserendo i valori dei due argomenti rispettivamente nel membro x e nel membro y. Aggiungiamo che la chiamata esplicita può essere utilizzata anche per invocare un  costruttore di default (è necessaria, per esempio, quando l'oggetto è creato all'interno di un'espressione), per esempio:
                throw
Error( )
;
(solleva un'eccezione e trasmette un oggetto della classe Error, creato con il costruttore di default).

   
Terminiamo questa sezione osservando che anche i tipi nativi hanno i loro costruttori di default (sebbene di solito non si usino), che però, quando servono, vanno esplicimente chiamati, come nel seguente esempio:
                int i = int();
i costruttori di default dei tipi nativi inizializzano le variabili con zero (in modo appropriato al tipo). Sono utili quando si ha a che fare con tipi parametrizzati (come i template, che vedremo più avanti), in cui non è noto a priori se al parametro verrà sostituito un tipo nativo o un tipo astratto.

 


 

Costruttori e conversione implicita

 

Un'attenzione particolare merita il costruttore con un solo argomento. In questo caso, infatti, il costruttore definisce anche una conversione implicita di tipo dal tipo dell'argomento a quello della  classe (ovviamente, spetta al codice di implementazione del costruttore assicurare che la conversione venga eseguita in modo corretto). Esempio:
     
      definizione del costruttore di point :   point::point(double d)
{x=d; y=d;}
definizione dell'oggetto p, istanza di point :
                                  point p = 3;
                                             è equivalente a : point p = point(3.0);

Notare che il numero 3 (che è di tipo int) è convertito implicitamente, prima a double, e poi nel tipo point (tramite esecuzione del costruttore, che lo utilizza per inizializzare l'oggetto p). Notare anche (per "chiudere il cerchio") che un'espressione del tipo point(3.0) è formalmente identica a un'operazione di casting in function-style (è persino ammessa la forma in C-style !).

Le conversioni implicite sono molto utili nella definizione degli operatori in overload (come vedremo prossimamente).

La conversione implicita può essere esclusa premettendo, nella dichiarazione (non nella definizione esterna) del costruttore lo specificatore explicit :
                       explicit point(double);
il casting continua invece ad essere ammesso (anche nella forma in  C-style), in quanto coincide puramente con la chiamata del costruttore.

 


 

Distruttori

 

I distruttori degli oggetti devono sottostare alle seguenti regole (ci rifaremo al solito esempio della classe point):

  1. devono avere lo stesso nome della classe preceduto da una tilde (~)
              prototipo: ~point( );
    definizione esterna:      point::~point( )  {......}

  2. non bisogna specificare il tipo di ritorno (neanche void)

  3. non ammettono argomenti

  4. ciascuna classe può avere al massimo un distruttore

  5. devono essere dichiarati come funzioni-membro pubbliche, in quanto sono sempre chiamati dall'esterno della classe a cui appartengono.

Come i costruttori, i distruttori non sono obbligatori; sono richiesti quando è necessario liberare risorse allocate dagli oggetti o ripristinare le condizioni preestistenti alla loro creazione. Se esiste, un distruttore è sempre chiamato automaticamente ogni volta che l'oggetto di cui fa parte sta per essere distrutto.

Quando più oggetti sono costruiti in sequenza, e poi sono distrutti contemporaneamente (per esempio se sono oggetti automatici che escono dal loro ambito di visibilità), i loro distruttori sono normalmente eseguiti in sequenza inversa a quella di costruzione.

 


 

Oggetti allocati dinamicamente

 

Se il programma non definisce direttamente un oggetto, ma un suo puntatore, il costruttore non entra in azione al momento della definizione del puntatore, bensì quando viene allocata dinamicamente la memoria per l'oggetto (caso 3. dell'elenco). Solito esempio:
      point* ptr;     costruisce la "variabile" puntatore ma non l'area puntata
ptr = new point; costruisce l'area puntata

la seconda istruzione dell'esempio esegue varie cose in una sola volta:

Quando si vuole che nella creazione di un oggetto sia eseguito un costruttore con argomenti, bisogna aggiungere, nell'istruzione di allocazione della memoria, l'elenco dei valori degli argomenti (fra parentesi tonde):
                       ptr = new point (3.5, 2.1);
questa istruzione cerca, fra i costruttori della classe point, quello con due argomenti di tipo double, e lo esegue al posto del costruttore di default .

   
Se si alloca dinamicamente un array di oggetti, sappiamo che la dimensione dell'array va specificata fra parentesi quadre dopo il nome della classe. Poichè il costruttore chiamato è unico per tutti gli elementi dell'array, questi vengono tutti inizializzati nello stesso modo. Nessun problema se si usa il costruttore di default (purchè sia disponibile):
                       ptr = new point [10];
ma, quando si vuole usare un costruttore con argomenti:
                       ptr = new point [10] (3.5, 2.1);
non sempre l'istruzione viene eseguita correttamente: anzitutto alcuni compilatori più antichi (come il Visual C++, vers. 6) non l'accettano; quelli che l'accettano la eseguono bene se il tipo è astratto (come nell'esempio di cui sopra), ma se il tipo è nativo, per es.:
                       ptr = new int [10] (3);
disponendo solo del costruttore di default, tutti gli elementi dell'array vengono comunque inizializzati con 0 (cioè la parte dell'istruzione fra parentesi tonde viene ignorata).

 
Gli  oggetti allocati dinamicamente non sono mai distrutti in modo automatico. Per ottenere che vengano distrutti, bisogna usare l'operatore delete. Es. (al solito ptr punta a oggetti della classe point):
        delete ptr; (per un singolo oggetto)           delete  [ ]  ptr; (per un array)

a questo punto viene eseguito, per ogni oggetto, il distruttore della classe point (se esiste) .

[p48]

 


 

Membri puntatori

 

Una particolare attenzione va rivolta alla programmazione dei costruttori e del distruttore di un oggetto che contiene membri puntatori.

Infatti, a differenza dal caso degli array, l'area puntata non è definita automaticamente e quindi (a meno che al puntatore non venga successivamente assegnato l'indirizzo di un'area già esistente) capita quasi sempre che l'area debba essere allocata nella memoria heap. e che questa operazione venga eseguita proprio da un costruttore dell'oggetto.

Analogamente, quando l'oggetto è distrutto (per esempio se è un oggetto automatico che va out of scope), sono del pari distrutti tutti i suoi membri, compresi i membri puntatori, ma non le aree puntate, che continuano ad esistere senza essere più raggiungibili (errore di memory leak).

Pertanto è indispensabile che sia lo stesso distruttore  dell'oggetto a incaricarsi di distruggere esplicitamente le aree puntate, cosa che può essere fatta solamente usando l'operatore delete. Esempio:

CLASSE COSTRUTTORE DISTRUTTORE
class Persona { Persona::Persona (int n) Persona::~Persona ( )
     char* nome; { {
     char* cognome;        nome = new char [n];        delete [ ] nome;
public:        cognome = new char [n];        delete [ ] cognome;
     Persona (int); } }
     ~Persona ( ); DEFINIZIONE DELL'OGGETTO NEL PROGRAMMA
.... altri metodi  };

Persona Tizio(25);

l'oggetto Tizio, istanza della classe Persona, viene costruito automaticamente nella memoria stack, e così pure i suoi membri. In aggiunta, il costruttore dell'oggetto alloca nella memoria heap due aree di 25 byte, e sistema i rispettivi indirizzi nei membri puntatori Tizio.nome e Tizio.cognome. Quando l'oggetto Tizio va out of scope, il distruttore entra in azione automaticamente  e, con l'operatore delete, libera la memoria heap allocata per le due aree. Senza il distruttore, sarebbe stata liberata soltanto la memoria stack occupata dall'oggetto Tizio e dai suoi membri puntatori , ma non l'area heap indirizzata da questi.

[p49]

 


 

Costruttori di copia

 

I costruttori di copia sono particolari costruttori che vengono eseguiti quando un oggetto é creato per copia. Ricordiamo brevemente in quali casi ciò si verifica:

Un costruttore di copia deve avere un solo argomento, dello stesso tipo dell'oggetto da costruire; l'argomento (che rappresenta l'oggetto esistente) deve essere dichiarato const (per sicurezza) e passato by reference (altrimenti si creerebbe una copia della copia!). Riprendendo il solito esempio, il costruttore di copia della classe point é:
                                            point::point(const point& q) {......}
e viene chiamato automaticamente ogni volta che si verifica una delle quattro circostanze sopraelencate.

Per esempio, se definiamo un oggetto p e lo inizializziamo con un oggetto preesistente q:
            point p = q ;
questa istruzione aziona il costruttore di copia, a cui é trasmesso q come argomento.

I costruttori di copia, come ogni altro costruttore, non sono obbligatori: se una classe non ne possiede, il C++ fornisce un costruttore di copia di default che esegue la copia membro a membro. Questo può essere soddisfacente nella maggioranza dei casi. Tuttavia, se la classe possiede dei membri puntatori, l'azione di default copia i puntatori, ma non le aree puntate: alla fine si ritrovano due oggetti i cui rispettivi membri puntatori puntano alla stessa area. Ciò potrebbe essere pericoloso, perché, se viene chiamato il distruttore di uno dei due oggetti, il membro puntatore dell'altro, che esiste ancora, punta a un'area che non esiste più (errore di dangling references).

Nell'esempio seguente una classe di nome A contiene, fra l'altro, un membro puntatore a int e un costruttore di copia che esegue le operazioni idonee ad evitare l'errore di cui sopra:
CLASSE                   COSTRUTTORE DI COPIA
              class A {         A::A(const A& a)
       int* pa;         {
public:                pa = new int ;
       A(const A&);                *pa = *a.pa ;
       ........  };         }

in questo modo, a seguito della creazione di un oggetto a2 per copia da un esistente oggetto a1:
                   A a2 = a1;
il costruttore di copia fa si che la variabile puntata
*a1.pa venga copiata in *a2.pa; senza il costruttore sarebbe copiato il puntatore a1.pa in a2.pa.

[p50]

 


 

Liste di inizializzazione

 

Quando un costruttore deve, fra l'altro, inizializzare i membri della propria classe, lo può fare tramite una lista di inizializzazione (introdotta dal segno ":" e inserita nella definizione del costruttore dopo la lista degli argomenti), la quale sostituisce le istruzioni di assegnazione (in effetti un costruttore non dovrebbe assegnare bensì solo inizializzare, anche se la distinzione può sembrare solo formale).

La sintassi di una lista di inizializzazione si desume dal seguente esempio:
CLASSE                   COSTRUTTORE
              class A { A::A(int p, double q) : m1(p), m2(0), r(q)
       int m1, m2; {
       double r;        .... eventuali altre operazioni....
public: }
       A(int,double);
       ........  };

Notare che alcuni membri possono essere inizializzati con valori costanti, altri con i valori degli argomenti passati al costruttore. L'ordine nella lista è indifferente; in ogni i caso i membri sono costruiti e inizializzati nell'ordine in cui appaiono nella definizione della classe.

E' buona norma utilizzare le liste di inizializzazione ogni volta che é possibile. Il loro uso é indispensabile quando esistono membri della classe dichiarati const o come riferimenti, per i quali l'inizializzazione è obbligatoria.

[p51][p51] [p51]

 


 

Membri oggetto

 

Riprendiamo ora ad esaminare l'elenco presentato all'inizio di questo capitolo e consideriano la costruzione e distruzione degli oggetti, quando sono membri  non statici di  una classe (caso 4. dell'elenco).

Sappiamo già che una classe può avere anche tipi classe fra i suoi membri; per esempio:

class A {                         class C {
       int aa;  ........  };        A ma;  
class B {        B mb;
       int bb;  ........  };        int mc;  ........  };

La classe C del nostro esempio viene detta classe composta, in quanto  contiene, fra i suoi membri, oggetti di altre classi (il membro-oggetto ma della classe A e il membro-oggetto mb della classe B).

Sappiamo inoltre che, creata un'istanza cc di C, le variabili corrispondenti ai singoli membri vanno indicate nel programma con espressioni del tipo: cc.ma.aa oppure cc.mb.bb (diritti di accesso permettendo).

Nel momento in cui un oggetto di una classe composta sta per essere costruito, e prima ancora che il suo costruttore completi l'operazione,  sono eseguiti automaticamente i costruttori che inizializzano i membri delle classi  componenti. Se esistono e si vogliono utilizzare i costruttori di default, non esiste problema. Ma se deve essere chiamato un costruttore con argomenti, ci si chiede in che modo tali argomenti possano essere passati, visto che il costruttore di un membro-oggetto non è chiamato esplicitamente.

In questi casi, spetta al costruttore della classe composta provvedere a che vengano eseguiti correttamente anche i costruttori delle classi  componenti. Per ottenere ciò, deve includere, nella sua lista di inizializzazione, tutti (e soli) i membri-oggetto che non utilizzano il proprio costruttore di default, ciascuno con i valori di inzializzazione che corrispondono esattamente (cioè con gli stessi tipi e nello stesso ordine) alla lista degli argomenti del rispettivo costruttore. Seguitando con il nostro esempio:
costruttore di  A :    A::A(int x)  : aa(x) {  ........  }
costruttore di  B : B::B(int x)  : bb(x) {  ........  }
costruttore di  C : C::C(int x, int y, int z)  : ma(z), mb(x), mc(y)  {  ........  }

   
Le classi  componenti A e B hanno anche una loro vita autonoma e in particolare possono essere istanziate con oggetti propri. In questo caso il costruttore di  C può generare i suoi membri-oggetto copiando oggetti già costruiti delle classi  componenti. Riprendendo l'esempio, un'altra forma del costruttore di  C potrebbe essere:

        C::C(int x, const A& a, const B& b)  : ma(a), mb(b), mc(x)  {  ........  }

dove gli argomenti a e b corrispondono a istanze già create rispettivamente di A e di B; in tale caso viene eseguito il costruttore di copia, se esiste, oppure di default viene fatta la copia membro a membro.

   
Quando un oggetto di una classe composta viene distrutto, vengono successivamente e automaticamente distrutti tutti i membri delle classi  componenti, in ordine inverso a quello della loro costruzione.

[p52][p52]

 


 

Array di oggetti

 

Gli elementi di un array di oggetti (caso 5. dell'elenco iniziale) vengono inizializzati, tramite il costruttore della classe comune di appartenenza, non appena l'array è definito.

Come al solito, non esiste nessun problema se si utilizza il costruttore di default:
                   point pt[5];

(costruisce 5 oggetti della classe point, invocando, per ciascuno di essi, il costruttore di default).

Se invece si vuole (o si deve, per mancanza del costruttore di default) utilizzare un costruttore con argomenti, bisogna considerare a parte il caso di costruttore con un solo argomento (o con più argomenti di cui uno solo required). Ricordiamo a questo proposito come si inizializza un array di tipo nativo:
                    int valori[] = {32, 53, 28, 85, 21};
nello stesso modo si può inizializzare un array di tipo astratto:
                    point pt[] = {2.3, -1.2, 0.0, 1.4, 0.5};
ma in questo caso ogni valore di inizializzazione , relativo a un elemento dell'array, viene passato come argomento al costruttore.
Ciò è possibile in quanto, grazie alla presenza del costruttore con un solo argomento, ogni valore è convertito implicitamente in un oggetto della classe point (chiamiamolo pn) e quindi l'espressione precedente diventa:
                    point pt[] = {p0, p1, p2, p3, p4};
l'inizializzazione in questa forma di un  array di un certo tipo, tramite elementi dello stesso tipo precedentemente costruiti, è sempre consentita, anche per i tipi astratti.

Non esiste invece alcuna possibilità di utilizzare costruttori con due o più argomenti.

[p53]

 


 

Oggetti non locali

 

Abbiamo già considerato i casi degli oggetti globali, degli oggetti nei namespace e dei membri statici delle classi (numero 6. dell'elenco iniziale), come casi particolari di oggetto singolo creato direttamente mediante la definizione del suo nome (vedere sezione: Costruttori). Sappiamo che tali oggetti non locali sono costruiti una sola volta, alla partenza del programma, e distrutti solo quando il programma termina.

Qui vogliamo solo aggiungere alcune considerazioni riguardo all'ordine di costruzione e distruzione di più oggetti:

Ne consegue che è molto "imprudente" inserire, nel codice del costruttore di un oggetto non locale, operazioni che coinvolgano oggetti definiti in altre translation unit (in particolare evitare istruzioni con cin e cout, in quanto non si può essere sicuri che gli oggetti globali delle classi di flusso di I/O siano già stati costruiti).

 


 

Oggetti temporanei

 

Abbiamo detto che un oggetto temporaneo (caso 7. dell'elenco iniziale) viene costruito per memorizzare risultati parziali durante la valutazione di un'espressione, e distrutto alla fine dell'espressione completa in cui compare (con il termine "espressione completa" si intende un'espressione che non sia sotto-espressione di un'altra, cioè, in pratica, un'intera istruzione di programma).

Finora abbiamo considerato soltanto operazioni fra tipi nativi, per i quali il problema della costruzione di un oggetto temporaneo non si pone. Ma, come vedremo nel prossimo capitolo, il C++ consente anche operazioni fra tipi astratti, tramite la possibilità di ridefinire, in overload, le funzioni che competono all'azione di molti operatori (overload degli operatori). Per esempio, si potrebbe ridefinire l'operatore di somma (+)  in modo che accetti fra i suoi operandi anche oggetti della classe classe point (si tratterebbe in questo caso di una somma "vettoriale", ottenuta mediante somma membro a membro delle coordinate dei punti):
                      point p = p1 + p2;
dove p1 e p2 sono istanze già create della stessa classe.

In questo caso è costruito l'oggetto temporaneo p1 + p2, che viene distrutto dopo che l'istruzione è stata eseguita. Ci chiediamo però: cosa succede se la classe point non ha un costruttore di default ? La risposta è che spetta al codice di implementazione della funzione, che definisce l'operatore di somma in overload, provvedere a che l'operazione sia eseguita correttamente (per esempio potrebbe definire un'istanza locale di point, con valori di inizalizzazione qualsiasi, usarla per memorizzare la somma di p1 e p2 membro a membro, e infine trasmetterla come valore di ritorno by value, da copiare in p).

In generale, tutte le volte che un'operazione crea un oggetto temporaneo, la funzione che compete a quell'operazione deve creare nel proprio ambito locale un corrispondente oggetto, che, in quanto costruito mediante definizione con un nome (categoria 1. del nostro elenco), non pone problemi, possegga o meno il costruttore di default.

 


 

Utilità dei costruttori e distruttori

 

Poiché in C++ ogni oggetto ha una sua precisa connotazione, caratterizzata da proprietà e metodi, i costruttori e i distruttori hanno in realtà un campo di applicazione molto più vasto della semplice inizializzazione o liberazione di risorse: in senso lato possono servire ogni qual volta un oggetto necessita di ben definite operazioni iniziali e finali, incapsulate nell'oggetto stesso. Per esempio, se l'oggetto consiste in una procedura di help, il costruttore potrebbe servire per creare la "finestra di aiuto", mentre il distruttore avrebbe il compito di ripristinare le condizioni preesistenti dello schermo.

 


 

Torna all'Indice