ProgrammingC++ Middle developer

Explain the differences between shallow and deep copy in C++ using the example of a container with dynamic memory. How to implement deep copying manually?

Pass interviews with Hintsage AI assistant

Answer.

The mechanics of copying objects in C++ is divided into shallow copy and deep copy. The difference is particularly important for classes with dynamically allocated memory.

Background

In C++, many data structures work with dynamic memory (new/delete). By default, the compiler generates a copy constructor and assignment operator that performs a byte-for-byte copy (shallow copy). This is fast but dangerous if the object manages external resources.

The Problem

Shallow copy copies only the addresses of dynamically allocated resources. When deleting one object, the memory will be freed, leaving the other instance with a "dangling" pointer. This results in double deletes, memory leaks, and crashes.

The Solution

Deep copying involves explicitly creating copies of all dynamic resources. To do this, the class must implement its own copy constructor and assignment operator to ensure a copy of each element.

Example code for a class with an array:

class DynArray { int* data; size_t size; public: DynArray(size_t n) : size(n), data(new int[n]) {} ~DynArray() { delete[] data; } // Deep copy 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]; } // Deep copy assignment 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; } };

Key features:

  • Shallow copy copies pointers, deep copy creates new instances of dynamic memory.
  • Deep copy requires implementing custom copying logic in the constructor and assignment operator.
  • Ignoring the necessity of deep copy leads to hard-to-trace bugs.

Trick Questions.

The compiler always correctly generates a copy constructor and assignment operator, right?

Answer:

Incorrect. For classes with dynamic resources, default copying is incorrect: both objects will own the same resource. Deep copy must be explicitly implemented when owning external resources.

Is it necessary to implement a destructor if only the deep copy constructor/assignment is written?

Answer:

Yes, otherwise there will be a memory leak: if you free memory in the user-defined copy constructor but do not implement a destructor, memory will not be freed when objects are destroyed.

Can std::vector store pointers and why can there be leaks when copying it?

Answer:

Yes, std::vector can store pointers. When copying such a std::vector, the pointers themselves are copied, not the objects they point to. This is shallow copy: if you need to deep copy all contents, you will need to manually copy each object and place them in memory independently.

Example:

std::vector<int*> v1; v1.push_back(new int(42)); std::vector<int*> v2 = v1; // Pointers are copied, not *int

Common mistakes and anti-patterns

  • Ignoring the need to implement the Rule of Three.
  • Copying pointers, believing it is a copy of the object.
  • Not freeing dynamic resources in the destructor.
  • Using shallow copy for classes with owned resources.

Real-life example

Negative case

A programmer implements a wrapper class for an array without overriding the copy constructor/assignment operator. As a result, both objects own the same memory; destroying one leads to a crash when accessing the other.

Pros:

  • Works fast (no copies).

Cons:

  • Very hard-to-trace runtime errors; presence of double free/segfault.

Positive case

A developer implements deep copying: contents of the array are copied, there is its own destructor and assignment operator with self-assignment protection.

Pros:

  • Safe copying and memory freeing.
  • Code is maintainable and extensible.

Cons:

  • Slightly more code and memory overhead.
  • More complex for classes with multiple dynamic resources.