CodeBlanks Logo Icon CodeBlanks

Rule of five

Learn the rule of five and resource management.

Rule of five

The rule of five says: if a class defines a destructor, copy constructor or copy assignment, it probably needs all five.

The five members are: ~T() (destructor), T(const T&) (copy constructor), T& operator=(const T&) (copy assignment), T(T&&) (move constructor) and T& operator=(T&&) (move assignment). If a class manages a resource (raw pointer, file handle) and you only define the destructor, copying does a shallow copy and causes a double-free error. That is why writing all five is the safest approach.

Rule of zero

In modern C++ the rule of zero is preferred: if a class holds only resource-managing members like std::string, std::vector or smart pointers, you do not need to define any special members.

The compiler automatically generates correct move and copy operations for those members. Example: class Student { std::string name; std::vector<int> grades; }; needs no special members and everything works automatically. With raw pointers you need the rule of five; with smart pointers the rule of zero suffices. That is why modern C++ avoids raw pointers.

Practice Add move members to the Buffer class

The Buffer class in the starting code already has a destructor, copy constructor and copy assignment (three of the rule of five). Add the move constructor and move assignment. In main move a into b with std::move and print b.data with std::cout.

Starter code

main.cpp
#include <iostream>
#include <string>
#include <utility>

class Buffer {
public:
    std::string data;
    Buffer(std::string d) : data(d) {}
    ~Buffer() {}
    Buffer(const Buffer& other) : data(other.data) {}
    Buffer& operator=(const Buffer& other) { data = other.data; return *this; }
    // Add move constructor and move assignment
};

int main() {
    Buffer a("Hello");
    // Move a into b and print b.data
    return 0;
}

Sample solution

main.cpp
#include <iostream>
#include <string>
#include <utility>

class Buffer {
public:
    std::string data;
    Buffer(std::string d) : data(d) {}
    ~Buffer() {}
    Buffer(const Buffer& other) : data(other.data) {}
    Buffer& operator=(const Buffer& other) { data = other.data; return *this; }
    Buffer(Buffer&& other) : data(std::move(other.data)) {}
    Buffer& operator=(Buffer&& other) { data = std::move(other.data); return *this; }
};

int main() {
    Buffer a("Hello");
    Buffer b(std::move(a));
    std::cout << b.data;
    return 0;
}

Expected output

Hello

Questions and answers

Review the lesson questions together with their answers. To test yourself, solve the lesson interactively.

1 Match each of the five members to its role

Term Meaning
~T() Destructor
T(const T&) Copy constructor
T(T&&) Move constructor
T& operator=(T&&) Move assignment

2 How many members does the rule of five cover?

main.cpp
~T(); T(const T&); T(T&&);
T& operator=(const T&); T& operator=(T&&);
  • Three
  • Two
  • Five Correct answer
  • Seven

Explanation: The rule of five covers five members.

3 Destructor signature

Question
~____() { /* release the resource */ }
Answer
~Buffer() { /* release the resource */ }

Explanation: The destructor is written as ~ClassName().

All lessons in Intermediate