Het kopiëren van objecten in C++ is verdeeld in oppervlakkige kopie (shallow copy) en diepe kopie (deep copy). Het verschil is vooral belangrijk voor klassen met dynamisch toegewezen geheugen.
In C++ werken veel datastructuren met dynamisch geheugen (new/delete). Standaard genereert de compiler een copy constructor en assignment operator die byte-voor-byte kopieert (shallow copy). Dit is snel, maar gevaarlijk als het object externe bronnen beheert.
Shallow copy kopieert alleen de adressen van dynamisch toegewezen bronnen. Bij het verwijderen van één object wordt het geheugen vrijgegeven, terwijl een ander exemplaar een "dangling" pointer overhoudt. Dit resulteert in double delete, geheugenlekken en crashes.
Diepe kopie houdt in dat een expliciete kopie van alle dynamische bronnen wordt gemaakt. Hiervoor moet je in de klasse zelf de copy constructor en assignment operator implementeren, zodat elke component wordt gekopieerd.
Voorbeeldcode voor een klasse met een array:
class DynArray { int* data; size_t size; public: DynArray(size_t n) : size(n), data(new int[n]) {} ~DynArray() { delete[] data; } // Diepe kopie constructor DynArray(const DynArray& other) : size(other.size), data(new int[other.size]) { for (size_t i = 0; i < size; ++i) data[i] = other.data[i]; } // Diepe kopie toewijzing DynArray& operator=(const DynArray& other) { if (this != &other) { delete[] data; size = other.size; data = new int[size]; for (size_t i = 0; i < size; ++i) data[i] = other.data[i]; } return *this; } };
Belangrijke kenmerken:
De compiler genereert altijd correct een copy constructor en assignment operator, toch?
Antwoord:
Onjuist. Voor klassen met dynamische bronnen is de standaard kopie niet correct: beide objecten bezitten dezelfde bron. Deep copy moet expliciet worden geïmplementeerd bij het bezitten van externe bronnen.
Moet ik een destructor implementeren als ik alleen een deep copy constructor/assignment heb geschreven?
Antwoord:
Ja, anders ontstaat er een geheugenlek: als je geheugen vrijgeeft in de gebruikers-definiete kopie constructor, maar geen destructor implementeert, wordt het geheugen niet vrijgegeven bij het vernietigen van de objecten.
Kan std::vector pointers opslaan en waarom kunnen er bij het kopiëren ervan geheugenlekken optreden?
Antwoord:
Ja, std::vector kan zonder problemen pointers opslaan. Bij het kopiëren van zo'n std::vector worden de pointers zelf gekopieerd, en niet de objecten waarnaar ze verwijzen. Dit is shallow copy: als een diepe kopie van de gehele inhoud nodig is, is het nodig om elke object handmatig te kopiëren en ze onafhankelijk in het geheugen te plaatsen.
Voorbeeld:
std::vector<int*> v1; v1.push_back(new int(42)); std::vector<int*> v2 = v1; // Pointers worden gekopieerd, niet *int
Een programmeur implementeert een klasse-wrapper voor een array zonder de copy constructor/assignment operator te overschrijven. Hierdoor bezitten beide objecten hetzelfde geheugen, en het vernietigen van één leidt tot een crash wanneer je toegang probeert te krijgen tot het andere.
Voordelen:
Nadelen:
Een ontwikkelaar implementeert diepe kopie: de inhoud van de array wordt gekopieerd, er is een eigen destructor en een assignment operator met bescherming tegen self-assignment.
Voordelen:
Nadelen: