Skip to content
AgileLogics
5 min read

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.