User Tools

Site Tools


appunti3s:costruttore

Differences

This shows you the differences between two versions of the page.

Link to this comparison view

Both sides previous revisionPrevious revision
appunti3s:costruttore [2017/07/09 15:40] profproappunti3s:costruttore [2020/06/08 22:19] (current) – external edit 127.0.0.1
Line 1: Line 1:
 +< < indice degli [[appunti3s:linguaggio_c|appunti sul linguaggio C++]]
  
 +
 +===== Costruttore =====
 +Nel linguaggio C++ si utilizzano dei "modelli" chiamati classi. 
 +Per capire cosa sia una classe e una funzione membro si deve __prima__ leggere 
 +
 +  * [[appunti3s:esempio_di_programmazione_orientata_agli_oggetti]]
 +  * [[appunti3s:funzioni_membro]]
 +
 +
 +Dato che la classe è un nuovo tipo di dato, essa deve descrivere, oltre alla struttura dei dati e delle sue funzioni membro, anche come (1)//riservare//, (2)//inizializzare// e infine (3)//liberare// la memoria occupata da ogni oggetto di tale classe. Il //costruttore// serve proprio per le prime due esigenze: riservare ed inizializzare la memoria. La liberazione, invece, è fatta dal //distruttore//.
 +
 +Il //costruttore// è una funzione che permette di realizzare (creare) gli oggetti in memoria, di un certo tipo di dato (la classe).
 +Il compilatore permette di usare un costruttore di default (predefinito), ma anche di dichiarare  diversi costruttori personalizzati che differiscono dal primo solo per i parametri che gli vengono passati.
 +
 +====Dichiarazione====
 +
 +La //dichiarazione// costruttore assomiglia a quella di una normale funzione membro, ma a differenza di questa:
 +  - non restituisce nulla (neppure void);
 +  - possiede lo stesso nome della classe a cui appartiene.
 +
 +Codice della chiamata del costruttore predefinito, per creare l'oggetto conto1
 +<code>
 +ContoCorrente conto1;
 +</code>
 +
 +Il codice appena visto deve essere inserito nel contesto di questo esempio: [[appunti3s:esempio_di_programmazione_orientata_agli_oggetti]]
 +
 +Codice della chiamata di un altro costruttore, con qualche argomento, per creare conto2 con un saldo iniziale di 90 euro.
 +<code>
 +ContoCorrente conto2(90.00);
 +</code>
 +
 +====Definizione====
 +Il costruttore costruisce l'oggetto nella memoria costruendo prima i dati membri, nell'ordine in cui sono dichiarati nella classe, e eseguendo per ultimo il codice del costruttore vero e proprio (il distruttore procede in ordine inverso). Per evitare confusione è utile usare lo stesso ordine usato per la dichiarazione dei membri, anche nella dichiarazione dei parametri del costruttore.
 +<code>
 +ContoCorrente::ContoCorrente(int saldo)  // definizione del costruttore
 +{
 +   mSaldo = saldo;  // inizializzazione dei membri dell'oggetto tramite assegnazione
 +                    // questo metodo è inefficiente :(
 +       // resto del codice...
 +}
 +</code>
 +
 +===La lista di inizializzazione dei membri===
 +C'è un modo molto più comodo ed efficiente di inizializzare i membri di un oggetto mettendo anche in evidenza l'ordine di inizializzazione dei membri appena descritto, ed è la cosiddetta //lista di inizializzazione//, in cui l'inizializzazione dei membri non viene fatta con un'assegnazione nel corpo del costruttore, ma prima delle parentesi graffe, utilizzando i //due punti// (:).
 +
 +<code>
 +ContoCorrente::ContoCorrente(int saldo)    // definizione del costruttore
 +       : mSaldo(saldo)                     // inizializzazione tramite lista di inizializzazione
 +{
 +    // resto del codice...
 +}
 +
 +</code>
 +
 +Questa tecnica, alternativa alle assegnazioni, non è obbligatoria per i tipi primitivi (come //int//) ma è comunque consigliata. Diventa indispensabile usarla per i seguenti tipi (che non possono rimanere non inizializzati):
 +  * per i membri costanti, 
 +  * per tutti i membri che non usano il costruttore predefinito (oggetti)
 +  * per tutti i reference.
 +
 +Usandola sempre si evita di sbagliare!
 +
 +Al momento della chiamata del costruttore, prima verranno eseguiti tutti gli inizializzatori, e, dopo, il codice del costruttore.
 +
 +===== Funzioni membro predefinite =====
 +Sono funzioni che esistono sempre, anche se il programmatore non le definisce. In questo modo si ottiene la garanzia che alcune operazioni di uso frequente siano possibili. Si tratta di operazioni necessarie all'allocazione/inizializzazione/distruzione di oggetti:
 +  * costruttore minimo <code>NomeClasse();</code>
 +  * distruttore  <code>~NomeClasse();</code>
 +  * costruttore di copia<code>NomeClasse (const NomeClasse&);</code>
 +  * operazione di assegnazione con copia <code>NomeClasse& operator= (const NomeClasse& o)</code>
 +
 +Oltre ad essere operazioni frequenti, si tratta anche di operazioni delicate, che spesso il programmatore deve ri-definire, per ottenere il risultato che desidera. Solo per fare alcuni esempi, il costruttore di copia viene usato quando:
 +  * si passa un oggetto per valore ad una funzione
 +  * una funzione deve restituire un oggetto
 +  * una funzione deve lanciare un oggetto come eccezione (throw)
 +Tutte le funzioni membro viste, essendo predefinite, non vengono nemmeno ereditate. [[appunti3s:polimorfismo]]
 +
 +==== Costruttore predefinito ====
 +
 +Il costruttore predefinito è usato per l'allocazione e l'inizializzazione dei dati membro di un oggetto (che si potrebbe chiamare //a//). Può essere invocato esplicitamente dal programmatore oppure automaticamente dall'operatore //new//.
 +<code>
 +NomeClasse a;    // costruttore predefinito : privo di argomenti!
 +</code>
 +
 +È utile definirlo quando si crea una nuova classe in una libreria, perché chi utilizzerà la libreria potrebbe creare un oggetto senza fornire adeguati elementi di inizializzazione ed un eventuale costruttore predefinito fornirebbe una certa garanzia sul contenuto del nuovo oggetto. 
 +Inoltre il costruttore predefinito può essere riutilizzato all'interno degli altri costruttori, fornendo la garanzia della funzionalità e di non dover riscrivere due volte le stesse cose...
 +  * //dichiarazione// del costruttore predefinito
 +    * <code>    NomeClasse ();</code>
 +  * definizione
 +    * <code>    NomeClasse::NomeClasse ()
 +      : // segue la lista di inizializzazione...
 +    {}</code>
 +
 +==== Distruttore predefinito ====
 +
 +<code>~NomeClasse(); // deve essere privo di argomenti</code>
 +È molto simile al precedente e viene invocato automaticamente quando un oggetto precedentemente creato esce dal suo campo di visibilità o deallocato con delete. Quindi a maggior ragione è importante che esista sempre. Va ricordato che il distruttore non ha mai nessun argomento.
 +  * //dichiarazione// del distruttore
 +    * <code>    ~NomeClasse ();</code>
 +  * definizione
 +    * <code>    NomeClasse::~NomeClasse ()
 +       {}</code>
 +==== Costruttore di copia predefinito ====
 +  * Il costruttore predefinito è usato per l'inizializzazione dei dati membro di un oggetto (//a//).
 +  * Anche il costruttore di copia predefinito inizializza (usando i dati membro di un altro oggetto esistente).
 +<code>
 +NomeClasse a();    // costruttore predefinito
 +NomeClasse b = a;  // costruttore di copia predefinito
 +</code>
 +Nel caso in cui un oggetto (a) contiene almeno un puntatore ad un altro oggetto %%(c)%%, debba essere duplicato (b), il costruttore di copia predefinito effettuerebbe solo una copia dei valori contenuti nei dati membro e verrebbero solo copiati gli indirizzi contenuti nei puntatori. 
 +Esisterebbero due nuovi oggetti (a e b), ma il secondo contiene dei puntatori allo stesso oggetto %%(c)%% a cui punta il primo e modificando il contenuto del secondo (b) si rischia di modificare il contenuto del primo (a).
 +Per questo motivo il costruttore di copia deve essere ridefinito nel caso che la classe abbia come membri: reference, costanti o altri oggetti di tipo classe. Questo perché tutti questi tipi non sono mai inizializzate automaticamente, a meno che siano di tipo [[static]]
 +
 +  * //dichiarazione// del costruttore con copia
 +    * <code>    NomeClasse (const NomeClasse&);</code>
 +  * definizione
 +    * <code>    NomeClasse::NomeClasse (const NomeClasse& o)
 +      : // segue la lista di inizializzazione...
 +    {}</code>
 +Il costruttore di copia viene usato dal programma sia nel passaggio di parametri __per valore__ sia nel momento della restituzione di un valore alla funzione chiamante. Perciç questo costruttore dovrebbe essere personalizzato dal programmatore.
 +Durante il passaggio di parametri __per valore__ ad una funzione, il programma deve usare il costruttore di copia predefinito per poter realizzare una copia degli oggetti passati. 
 +È bene comunque ricordare che in alternativa al passaggio per valore si potrebbe passare l'oggetto usando un reference costante. Questo eviterebbe anche l'allocazione di nuova memoria...
 +==== Operatore assegnazione di copia predefinito ====
 +  * Il costruttore predefinito è usato per l'inizializzazione dei dati membro di un oggetto (//a//).
 +  * Anche il costruttore di copia predefinito inizializza (usando i dati membro di un altro oggetto esistente).
 +  * L'assegnazione tra oggetti dello stesso tipo, dà luogo invece ad una copia tra oggetti esistenti. 
 +
 +<code>
 +NomeClasse a();    // costruttore predefinito
 +NomeClasse b = a;  // costruttore di copia predefinito
 +NomeClasse c;
 +c = b;         //assegnazione di copia predefinita
 +</code>
 +Nonostante vi sia una differenza sostanziale, l'operazione di assegnazione opera in modo simile al costruttore di copia, dovendo inizializzare tutti i dati membro di a, in base a quelli di b. Per tale motivo esso deve essere ridefinito nel caso che la classe abbia come membri: reference, costanti o altri oggetti di tipo classe. Questo perché tutti questi tipi non sono mai inizializzate automaticamente, a meno che siano di tipo [[static]]
 +
 +  * //dichiarazione// dell'operatore assegnazione con copia
 +    * <code>    NomeClasse& operator= (const NomeClasse& o)</code>
 +  * //definizione//
 +    * <code>    NomeClasse& NomeClasse::operator= (const NomeClasse& o)
 +    {
 +     //...
 +     return *this;
 +    }</code>
 +    
appunti3s/costruttore.txt · Last modified: 2020/06/08 22:19 by 127.0.0.1