This is my learning notes from the course - Mastering C++ Standard Library Features.
Rule of Five and Rule of Zero
- How to make sure that your classes properly expose move operations
- Rule of three (before C++11)
- Rule of five and rule of zero (C++11 and later)
Rule of Three
- Before C++11, the rule of three claims that:
- If you have a class explicitly defining one of the following:
- Destructor
- Copy constructor
- Copy assignment operator
- Then, it should probably define all three
- If you have a class explicitly defining one of the following:
- This prevents mistake and oversights when implementing classes that manage resources
To show an example of the “rule of three” in action, imagine some sort of file_handle
pointer type that is a “resource” which can be acquired, released, and shared.
1 | struct file_handle; |
Imagine that we want to create a RAII wrapper around the C-like file_handle
that will automatically deal with resource acquisition/release and with ownership sharing.
1 | class file |
As soon as you have a user-defined destructor, you should probably define copy operations, such as copy constructor and copy assignment operator, as well (following the rule of three).
Explicitly defining copy operations prevents move operations
from being automatically generated by the compiler. Later we’ll see how to explicitly define them using the “rule of five“.
Rule of Five
After C++11, the rule of three was extended to the rule of five because of move operations
.
- The rule of five claims that:
- If you have a class explicitly defining one of the following:
- Destructor
- Copy constructor
- Copy assignment operator
- Move constructor
- Move assignment operator
- Then, it should probably define all five
- If you have a class explicitly defining one of the following:
Using the same example as before, let’s now follow the “rule of five“ to implement move operations
.
1 |
|
Rule of Zero
- The rule of zero encourages you to avoid implementing any copy/move operations for your classes, and to rely on the value semantics of existing types
- The idea is that custom copy/move/destructor are usually implemented to deal with resource management and ownership - if that’s the case, then the Single Responsibility Principle (SRP) encourages to extract this functionality to a separate class
In the previous code examples, the file
class was dealing with multiple tasks: managing the file_handle
resource and providing the _flags
member variable.
Let’s apply the “rule of zero“ to the file
class: the resource management logic can be extract to a separate shared_file_handle
class that follows the “rule of five“, while file
becomes very simple (following the SRP).
1 |
|
Custom Deleters
References:
https://en.cppreference.com/w/cpp/memory/unique_ptr
https://en.cppreference.com/w/cpp/memory/unique_ptr/unique_ptr
https://en.cppreference.com/w/cpp/memory/shared_ptr/shared_ptr
1 |
|