A class holds a raw pointer to a heap buffer. What goes wrong with the default copy constructor? Explain the rule of three, the rule of five and the rule of zero.
Show the explanation
The default copy is shallow. The compiler-generated copy constructor and copy assignment copy every member as it is. For char *data, that copies the address, so two objects now point to the same buffer:
- When the first object's destructor runs, it calls
delete[] data. - The second object then holds a dangling pointer. Its destructor deletes the buffer again, which is undefined behaviour (a double free).
- Changing one object also changes the "other".
A deep copy allocates a new buffer and copies the contents:
class Buffer {
size_t n; char *data;
public:
explicit Buffer(size_t n) : n(n), data(new char[n]{}) {}
~Buffer() { delete[] data; }
Buffer(const Buffer &o) : n(o.n), data(new char[o.n]) { std::copy(o.data, o.data + n, data); }
Buffer &operator=(Buffer o) { swap(o); return *this; } // copy-and-swap: handles self-assignment and is exception safe
Buffer(Buffer &&o) noexcept : n(o.n), data(o.data) { o.n = 0; o.data = nullptr; }
void swap(Buffer &o) noexcept { std::swap(n, o.n); std::swap(data, o.data); }
};(Taking the parameter by value means this one operator handles both copy assignment and move assignment.)
Rule of three (C++98). If a class needs a custom destructor, copy constructor or copy assignment operator, it almost certainly needs all three, because they all manage the same resource.
Rule of five (C++11). Add the move constructor and move assignment. A move "steals" the pointer and leaves the source empty, which is O(1) instead of a full copy. Mark moves noexcept so that std::vector uses them when it grows. Note: declaring a copy operation or a destructor stops the compiler from generating move operations automatically.
Rule of zero (preferred). Do not manage raw resources in everyday classes. Use members that already manage themselves, such as std::vector<char>, std::string or std::unique_ptr<char[]>. Then the compiler-generated special members are already correct and you write none of the five. Only small resource-owning classes, like the one above, should need the rule of five.
Original UniCircle question. Last reviewed 22 Oct 2026.
