Shallow copy copies only the values of the class members, including pointers - but does not copy the data pointed to by these pointers. Deep copy creates new copies of the data located at the pointers, preventing shared memory ownership and double freeing.
Example:
class Buffer { public: Buffer(size_t size) : size(size), data(new int[size]) {} // Shallow copy (incorrect) Buffer(const Buffer& other) : size(other.size), data(other.data) {} // Deep copy (correct) 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; } ~Buffer() { delete[] data; } private: size_t size; int* data; };
If the class contains a pointer to a dynamic array, why is it not enough to copy-paste the default copy constructor and operator=?
Correct answer:
Default copying implements shallow copying. Two objects will point to the same array, and when destroyed there will be an attempt to free the same memory twice (double free), which will lead to a crash.
Story In a large server application, a custom Image class was implemented that managed a dynamic pixel buffer. The developers did not implement deep copying, and when transferring Image objects by value between threads, double freeing occurred, leading to crashes every few days.
Story In a student project, a Field container was used which contained a pointer to a dynamically allocated array of cells. When resizing the array, the copying constructor only copied the pointer, and both containers operated on the same memory area, resulting in inconsistent data and hard-to-find bugs.
Story In old C++ code of a graphics engine, a Texture class was implemented without its own destructor and assignment operator, leading to memory leaks when copying and assigning temporary texture objects.