Shallow copy (copia superficiale) – copia solo i valori dei campi dell'oggetto, inclusi i puntatori, ma non duplica la memoria allocata dinamicamente. Di conseguenza, entrambi gli oggetti puntano alla stessa area di memoria. Questo può portare a errori durante la modifica dei dati o il rilascio della memoria.
Deep copy (copia profonda) – crea una copia completa di tutte le risorse a cui fa riferimento l'oggetto, in modo che ciascun oggetto possieda la propria copia dei dati.
Quando è necessaria la deep copy:
Esempio di implementazione di deep copy:
class ArrayHolder { public: ArrayHolder(size_t size) : sz(size), data(new int[size]) {} ~ArrayHolder() { delete[] data; } // Costruttore di copia profonda ArrayHolder(const ArrayHolder& other) : sz(other.sz), data(new int[other.sz]) { std::copy(other.data, other.data + other.sz, data); } // Assegnazione di copia profonda ArrayHolder& operator=(const ArrayHolder& other) { if (this != &other) { delete[] data; sz = other.sz; data = new int[sz]; std::copy(other.data, other.data + sz, data); } return *this; } private: size_t sz; int* data; };
Domanda: Funzionerà correttamente il costruttore di copia se in esso viene semplicemente copiato il valore del puntatore?
Risposta: No, questo porterà a una doppia eliminazione della memoria quando entrambi gli oggetti vengono distrutti. È necessario implementare la copia profonda per una corretta gestione delle risorse.
Storia
In uno dei progetti, per trasferire array tra i moduli, venivano utilizzati oggetti di una classe in cui il costruttore di copia predefinito copiava solo il puntatore (shallow copy). Di conseguenza, dopo l'uscita di un oggetto temporaneo dal contesto, l'array veniva distrutto, mentre l'oggetto principale rimaneva con un puntatore "appeso", portando a comportamenti indefiniti e crash dell'applicazione.
Storia
In una libreria per lavorare con le immagini, venivano ripetutamente copiati oggetti contenenti buffer allocati dinamicamente. A causa dell'assenza di deep copy, dopo il rilascio della memoria in un oggetto, un altro accedeva all'area liberata. Si manifestavano sia danni ai dati che crash durante l'eliminazione dell'oggetto.
Storia
Durante l'integrazione di codice obsoleto con standard moderni, nelle vecchie classi non erano stati implementati né il costruttore di copia né l'operatore di assegnazione. L'uso di queste classi con contenitori della libreria standard portava a inattesi crash, poiché i contenitori copiavano gli oggetti superficialmente.