Costruttore di copia e operatore di assegnazione di copia sono necessari per lavorare con oggetti che possiedono risorse (ad esempio, memoria dinamica). Per impostazione predefinita, il compilatore genererà una copia campo per campo, il che non è sicuro per un puntatore raw:
class Buffer { public: Buffer(size_t size) { data = new int[size]; this->size = size; } ~Buffer() { delete[] data; } // Implementazione corretta del costruttore di copia Buffer(const Buffer& other) : size(other.size) { data = new int[size]; std::copy(other.data, other.data + size, data); } // Operatore di assegnazione corretto Buffer& operator=(const Buffer& other) { if (this != &other) { delete[] data; size = other.size; data = new int[size]; std::copy(other.data, other.data + size, data); } return *this; } private: int* data; size_t size; };
Altrimenti, durante la copia di due oggetti, uno libera la memoria mentre l'altro rimane con un puntatore pendente (doppia liberazione o uso dopo liberazione).
Cosa succede se dichiari esplicitamente solo il costruttore di copia, ma non l'operatore di assegnazione? Quando è necessario e quando no?
Risposta: Se è dichiarato solo il costruttore di copia, ma non è dichiarato l'operatore di assegnazione, il compilatore genererà un operatore di assegnazione predefinito (copia bit per bit), il che è pericoloso se l'oggetto contiene risorse dinamiche, cioè distruggerà lo stesso puntatore due volte.
Nel caso di gestione della memoria, è necessario implementare entrambi: sia il costruttore di copia che l'operatore di assegnazione, per evitare errori di copia/fughe di memoria.
Storia
Nel media server, durante la copia di un oggetto buffer, è stato utilizzato il costruttore di copia, ma è stato dimenticato l'operatore di assegnazione. Quando un buffer veniva assegnato a un altro, si verificava una doppia liberazione della memoria durante la distruzione degli oggetti. L'errore si è manifestato durante i test di stress.
Storia
Nel progetto di logistica dei trasporti, l'operatore di assegnazione predefinito copiavano una struttura con un puntatore a un array di coordinate. Dopo l'eliminazione di un oggetto, il secondo accedeva alla memoria liberata, causando errori di segmentazione.
Storia
In uno dei progetti con risorse grafiche, è stato dimenticato di implementare il costruttore di copia durante il passaggio di oggetti tra thread. Il passaggio per valore portava a una copia superficiale, e dopo la modifica dell'oggetto in un altro thread si verificava una corruzione dei dati e un arresto anomalo del programma.