C++ and the Rule of Zero, Three, and Five

The Rule of Zero, Three, and Five

TL;DR

If you define the destructor, copy constructor, or move constructor, then all three should be defined.

If you don’t define any of those, and you also do not define the copy assignment operator nor the move assignment operator, then you don’t need to define any of the five.

However, the best practice is to always define all 5 of the rules (i.e. explicitly specify what you want for a copy/move constructor, copy/move assignment operator, and virtual destructor). If you just want the default, then you can explicitly declare them as defaults in the header. If you need to customize or prevent their action, then define or delete as necessary.

The Rule of Five (C++11 and beyond)

If you define a destructor, copy constructor, or copy assignment operator, then your class will not support move semantics, unless you explicitly add them.

If you wanted to just use the default destructor and constructors with copy and move semantics you can do this:

class Robot
{
 public:
  Robot();                                  // Default Constructor

  // These are the five:
  Robot(const Robot&) = default;            // Copy Constructor
  Robot(Robot&&) = default;                 // Move Constructor
  Robot& operator=(const Robot&) = default; // Copy Assignment Operator
  Robot& operator=(Robot&&) = default;      // Move Assignment Operator
  virtual ~Robot() = default;               // Destructor
};

The Rule of Three (before C++11)

If you define a destructor, copy constructor, or copy assignment operator, then the compiler will add all three and they will probably be wrong (because what the compiler does will probably be different than what you did in the one[s] you defined).

The virtual destructor is very important for classes that will be extended. This is needed, because it is the only way that this parent class destructor will be called when a child object is destroyed, and the parent should cleanup after itself.

If you wanted to just use the default destructor and constructors with copy semantics, but remove move semantics then (notice ‘delete’):

class Robot
{
 public:
  Robot();                                  // Default Constructor

  // These are the three:
  Robot(const Robot&) = default;            // Copy Constructor
  Robot(Robot&&) = delete;                  // Delete Move Constructor
  Robot& operator=(const Robot&) = default; // Copy Assignment Operator
  Robot& operator=(Robot&&) = delete;       // Delete Move Assignment Operator
  virtual ~Robot() = default;               // Destructor
};

The Rule of Zero

If you don’t have custom logic for a destructor, copy constructor, move constructor, copy assignment operator, or move assignment operator, then don’t define them.

The default constructors and destructors with copy semantics:

class Robot
{
 public:
  Robot();                                  // Default Constructor

  // These are the five:
  Robot(const Robot&) = delete;             // Delete Copy Constructor
  Robot(Robot&&) = delete;                  // Delete Move Constructor
  Robot& operator=(const Robot&) = delete;  // Delete Copy Assignment Operator
  Robot& operator=(Robot&&) = delete;       // Delete Move Assignment Operator
  virtual ~Robot() = default;               // Destructor
};

Keeping it Simple (C++11 and beyond)

As explained in the “Too Long; Didn’t Read” section at the top:

The best practice is to always define all 5 of the rules (i.e. explicitly specify what you want for a copy/move constructor, copy/move assignment operator, and virtual destructor). If you just want the default, then you can explicitly declare them as defaults in the header. If you need to customize or prevent their action, then define or delete as necessary.

This pretty much means to start with the defaults. If it doesn’t make sense to support copy or move then explicitly delete them. If you decide to implement a custom definition, then define or delete the others. Use logic when deciding what to do with the destructor.

Start with the defaults:

class Robot
{
 public:
  Robot();                                  // Default Constructor

  // These are the five:
  Robot(const Robot&) = default;            // Copy Constructor
  Robot(Robot&&) = default;                 // Move Constructor
  Robot& operator=(const Robot&) = default; // Copy Assignment Operator
  Robot& operator=(Robot&&) = default;      // Move Assignment Operator
  virtual ~Robot() = default;               // Destructor
};

References

CPP References Site

WikiPedia

Clean Code book

Advertisements