Shallow Copy vs Deep Copy in C++
Understand shallow vs deep copy in C++, why shallow copies cause double-free bugs, and how the Rule of Three and RAII give you safe, independent object copies.
Expected Interview Answer
A shallow copy duplicates an object's member values as-is, so pointer members end up pointing to the same underlying memory, while a deep copy allocates fresh memory and copies the pointed-to data so each object owns an independent resource.
The compiler-generated copy constructor and copy assignment operator perform member-wise (shallow) copies. For classes that manage raw resources like heap memory, file handles, or sockets, this shared ownership causes double-free crashes, dangling pointers, and unintended aliasing. A deep copy fixes this by allocating new storage and copying the contents, so destroying one object never invalidates another. Following the Rule of Three, if you write a custom destructor you almost always need a custom copy constructor and copy assignment operator too.
- Prevents double-free and dangling pointer bugs
- Gives each object independent ownership of its resources
- Makes value semantics safe and predictable
- Avoids accidental aliasing between copies
- Required for correct Rule of Three / Rule of Five classes
AI Mentor Explanation
Shallow copy is like two scorers sharing one physical scorebook: both hold the same pages, so a note one scorer scribbles instantly changes what the other sees, and if one tears the book out nobody has it. Deep copy is each scorer getting their own photocopied scorebook — identical numbers, but edits and damage stay entirely separate between the two teams' records.
Step-by-Step Explanation
Step 1
Identify managed resources
Find members that own raw pointers, heap allocations, file handles or other resources needing exclusive ownership.
Step 2
See what the default copy does
The implicit copy constructor copies each member value, so a pointer member is copied by address — both objects now share the same memory (shallow).
Step 3
Write a deep copy constructor
Allocate new storage and copy the pointed-to contents so the new object owns an independent resource.
Step 4
Provide copy assignment
Implement operator= to release the existing resource, allocate fresh memory, and copy the data, guarding against self-assignment.
Step 5
Prefer RAII containers
Use std::string, std::vector, or smart pointers so correct copy semantics come for free and you avoid manual memory management.
What Interviewer Expects
- Clear distinction between copying pointers versus copying pointed-to data
- Awareness of double-free and dangling pointer risks with shallow copies
- Knowledge of the Rule of Three / Rule of Five
- Ability to write a correct deep copy constructor and assignment operator
- Preference for RAII types over manual memory management
Common Mistakes
- Believing the default copy constructor performs a deep copy
- Writing a destructor but forgetting the copy constructor and assignment operator
- Omitting the self-assignment check in operator=
- Leaking the old resource before reassigning in operator=
- Confusing deep copy with move semantics
Best Answer (HR Friendly)
“A shallow copy just duplicates the surface of an object, so two copies end up sharing the same underlying data and can interfere with each other. A deep copy makes a completely independent duplicate, so changing or deleting one has no effect on the other.”
Code Example
#include <cstring>
#include <algorithm>
class Buffer {
char* data;
std::size_t size;
public:
Buffer(const char* s)
: size(std::strlen(s)) {
data = new char[size + 1];
std::strcpy(data, s);
}
// Deep copy constructor: allocate new memory
Buffer(const Buffer& other)
: size(other.size) {
data = new char[size + 1];
std::strcpy(data, other.data);
}
// Deep copy assignment with self-assignment guard
Buffer& operator=(const Buffer& other) {
if (this == &other) return *this;
char* fresh = new char[other.size + 1];
std::strcpy(fresh, other.data);
delete[] data; // release old resource
data = fresh;
size = other.size;
return *this;
}
~Buffer() { delete[] data; } // no double-free: each owns its data
};Follow-up Questions
- What is the Rule of Three, and how does the Rule of Five extend it?
- How do move semantics differ from a deep copy?
- Why does operator= need a self-assignment check?
- How do std::vector and std::string give you correct copies for free?
- What is a dangling pointer and how does a shallow copy create one?
MCQ Practice
1. What does the compiler-generated copy constructor do with a raw pointer member?
The implicit copy constructor performs a member-wise copy, so the pointer's address is copied and both objects point to the same allocation — a shallow copy.
2. Which problem most directly results from a shallow copy of an object owning heap memory?
Both copies hold the same pointer, so each destructor tries to free the same block, causing a double-free.
3. The Rule of Three says that if you define one of these, you likely need all three. Which set is it?
Managing a resource that requires a custom destructor almost always requires a custom copy constructor and copy assignment operator as well.
Flash Cards
Shallow copy — Member-wise copy; pointer members share the same memory, so copies alias the same resource.
Deep copy — Allocates fresh memory and copies the pointed-to data, giving each object independent ownership.
Rule of Three — If you define a destructor, copy constructor, or copy assignment, you usually need all three.
Why shallow copies crash — Two objects share one pointer; both destructors free it, causing a double-free and dangling pointers.
Easiest way to get deep copies — Use RAII types like std::string, std::vector, or smart pointers instead of raw pointers.