De copy constructor en de copy assignment operator zijn nodig voor het werken met objecten die resources bezitten (zoals dynamisch geheugen). Standaard genereert de compiler een member-wise kopie, wat onveilig is voor een raw pointer:
class Buffer { public: Buffer(size_t size) { data = new int[size]; this->size = size; } ~Buffer() { delete[] data; } // Correcte implementatie van de copy constructor Buffer(const Buffer& other) : size(other.size) { data = new int[size]; std::copy(other.data, other.data + size, data); } // Correcte copy assignment operator 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; };
Anders kunnen twee objecten bij het kopiëren één het geheugen vrijgeven, terwijl het andere met een dangling pointer blijft (double free of use after free).
Wat gebeurt er als je alleen de copy constructor declareert, maar geen copy assignment operator? Wanneer is deze nodig, en wanneer niet?
Antwoord: Als alleen de copy constructor is gedeclareerd, maar de copy assignment operator niet, zal de compiler een standaard copy assignment operator genereren (bitwise kopie), wat gevaarlijk is als het object dynamische resources bevat, d.w.z. het zal dezelfde pointer twee keer vrijgeven.
In het geval van geheugenbeheer moet je beide implementeren: zowel de copy constructor als de copy assignment operator, om fouten bij het kopiëren/geheugenlekken te vermijden.
Verhaal
In een mediaserver werd bij het kopiëren van een bufferobject de copy constructor gebruikt, maar men vergat de copy assignment operator. Toen één buffer aan een andere werd toegewezen, vond er dubbele geheugenvrijgave plaats bij het vernietigen van objecten. De fout kwam naar voren tijdens stress-tests.
Verhaal
In een project voor transportlogistiek kopiëerde de standaard copy assignment operator een structuur met een pointer naar een array van coördinaten. Nadat één object was verwijderd, probeerde het andere toegang te krijgen tot het vrijgegeven geheugen, wat segmentation faults veroorzaakte.
Verhaal
In een van de projecten met grafische resources vergaten ze de copy constructor te implementeren bij het doorgeven van objecten tussen threads. Doorgeving per waarde leidde tot shallow copy, en na wijziging van het object in een andere thread ontstond er schade aan de gegevens en crashte het programma.