A volte gli oggetti vengono ricopiati
automaticamente. Questo accade quando:
- Un oggetto passa un valore ad una funzione
- Un oggetto restituisce un valore da una funzione
- Inizializza un altro oggetto attraverso un inizializzatore
- 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