The Rule of Three, Five, and Zero
Prerequisites: Move semantics & rvalue references
If a class manages a raw resource directly (rare in modern C++, but it happens), it needs to define — or explicitly delete — five special member functions together: the destructor, copy constructor, copy assignment operator, move constructor, and move assignment operator (the Rule of Five; C++98 only had the first three, hence "Rule of Three").
class Buffer {
private:
int *data;
public:
Buffer(size_t size) : data(new int[size]) {}
~Buffer() { delete[] data; }
Buffer(const Buffer &other) = delete; // no copying
Buffer &operator=(const Buffer &other) = delete; // no copying
Buffer(Buffer &&other) noexcept { /* transfer ownership */ }
Buffer &operator=(Buffer &&other) noexcept { /* ... */ return *this; }
};
The far more common Rule of Zero: design classes to hold only
members that already manage themselves correctly (a std::vector,
a std::unique_ptr, a plain int) — then the compiler-generated
special members are already correct, and none of this needs to be
written by hand at all.
Common mistakes
- Writing a destructor that frees a raw resource, but relying on the compiler's default copy constructor — that default does a shallow copy of the pointer, so both copies then try to free the same memory (a double-free) once each is destroyed.