lunedì 3 dicembre 2012

Definire un Costruttore Copia

A volte gli oggetti vengono ricopiati automaticamente. Questo accade quando:
  1. Un oggetto passa un valore ad una funzione
  2. Un oggetto restituisce un valore da una funzione
  3. Inizializza un altro oggetto attraverso un inizializzatore
  4. Fornisce un singolo argomento al costruttore dell'oggetto
La copia viene effettuata attraverso una speciale funzione membro chiamata costruttore copia. Come i costruttori e i distruttori, una copia di default viene fornita se non scritta appositamente per l'oggetto. La copia di default semplicemente copia i valori di ogni dato membro a dati membro dello stesso nome nel nuovo oggetto. Per le classi più semplice il costruttore copia di default è più che sufficiente. Comunque quando si ha una classe con dati membro che puntano ad un valore su heap, solitamente viene scritto un proprio costruttore copia. La ragione è che il costruttore di default si limiterebbe a copiare i puntatori senza modificarne il contenuto, generando così una copia vuota, per la quale i blocchi di memoria non sono cambiati e puntano alle stesse locazioni dell'oggetto originale. Per fare un esempio specifico. Se non fosse stato scritto un proprio costruttore copia nel programma Dati membro su Heap, il programma avrebbe automaticamente creato una copia di ctopo chiamata copia che esiste in testCopiaCostruttore().
testCopiaCostruttore(ctopo);
Il dato membro m_pNome di copia avrebbe puntato esattamente allo stesso oggetto string su heap di ctopo m_pNome. Perché si crea un problema?
Al termine della funzione testCopiaCostruttore(), il distruttore di copia viene chiamato, liberando la memoria su heap alla quale puntava il dato membro m_pNome di copia. A causa di ciò ctopo m_pNome avrebbe puntato ad una memoria libera pertanto il dato membro si sarebbe trasformato in un puntatore appeso. Ciò di cui si ha veramente bisogno nel costruttore copia è di produrre un nuovo oggetto con il proprio segmento di memoria su heap alla quale puntare, una copia profonda. Questo è quello che viene fatto con la seguente definizione del costruttore copia, il quale sovraccarica il default fornito dal compilatore.

Roditore(const Roditore& c)
{ cout << “Copia costruttore”;
m_pNome = new string;
*m_pNome = c.DaiNome();
}
Così come il primo, anche in costruttore copia deve avere lo stesso nome della classe, e non restituisce alcun valore, ma accetta un riferimento ad un oggetto della classe, l'oggetto che deve essere copiato. Il riferimento è quasi sempre una costante che protegge l'oggetto originale dall'essere modificato durante il processo di copia. Quando viene chiamato testCopiaCostruttore() passando ctopo come valore della funzione, viene richiamato il costruttore copia. Appare la scritta Costruttore Copia sullo schermo; quindi viene creato un nuovo oggetto Roditore (la copia) che accetta i riferimenti originali in c. Con la linea m_pNome = new string; il costruttore alloca un nuovo blocco di memoria su heap al quale punta m_pNome dato membro per la copia. Nella riga successiva *m_pNome =c.DaiNome(); il costruttore copia acquisisce una copia dell'oggetto stringa che equivale a “Topolino” dall'originale scrivendolo sul nuovo blocco di memoria. Come risultato una copia profonda di ctopo viene creata, quella usata in testCopiaCostruttore(). Quando testCopiaCostruttore() termina, la copia del Roditore usata dalla funzione viene distrutta, lasciando l'oggetto originale al suo posto su heap.

Nessun commento:

Posta un commento