What is the rule of three?
And related to this: What is the rule of five? And what is the rule of zero?
When you need to implement either a destructor, a copy constructor, of an assignment operator, then the rule of three says that you really need to implement all three of these methods. If you don’t, then the compiler will provide a default implementation for you. And if you’re declaring and implementing a destructor, for example, because you need to clean-up some resources, then you’re almost certainly going to want to manage those resources when you make a copy of your object and when you assign one object instance to another.
The rule of five is a more modern version of the rule of three and it adds two more methods that should be implemented together with the other three. This involve move semantics and the methods are the move copy constructor and the move assignment operator.
The rule of zero is really just the same thing stated the other way around. This says that when you don’t need these methods, then don’t declare any of them. But what if you have a class that otherwise would not need these methods but because it’s intended to serve as a base class, then it needs a virtual destructor? In this case, you’re adding a destructor not for managing resources, but just to enable polymorphism to work properly through the base class. When you have to declare a destructor like this, then the rule of zero says that you should go ahead and declare the other four methods, the copy constructor, the move copy constructor, the assignment operator, and the move assignment operator, and specify that they should all be default. This will signal the compiler to provide them for you as if you had not declared any of them.
Listen to this QA episode for more or you can read the full transcript below.
Thank you for your review and comments. If you’d like me to read your review, well, the first step is you have to leave a review. The thing I liked about this review was the understanding. It’s a big goal for each episode to relate to something that you can understand.
Okay on to the question this week. Very few things are actually rules and normally when I think of rules in C++, the first thing that comes to mind are the compiler syntax rules and linker rules. If you get these wrong, your program won’t build. Either it won’t compile or it won’t link into the final product.
The rule of three is different because the compiler usually won’t warn you when you forget. And your program will almost certainly not work properly.
You already know that you can declare classes and structs with member methods. But did you know that the compiler will write some methods for you if you leave them out? This is not a bad thing and many times, the compiler generated methods will do what you need.
You normally run into problems when you try to provide your own implementation for some of these methods.
Let me give you an example. Let’s say you have a robot that knows how to bake a cake. And you were very careful with this robot to make sure that it never forgets to turn the oven off when it’s done. The moment it takes the cake out of the oven, it pushes the bake button.
Now some ovens have a special off button but your oven designers thought that was a waste of buttons and decided to have a single button that when pressed turns the oven on and then when pressed again turns the oven off.
Your robot is quite the success and you decide to build another one. After all, you should be able to bake two cakes at the same time and another robot would allow you to manage the baking in parallel. That just means that each robot is responsible for its own cake and they’re both in the kitchen at the same time.
The problem here is that they’re sharing the same oven. This is fine for the actual baking of the first cake. But what about the second one?
Let’s say the first robot gets access to some important tools such as the mixing bowl while the second robot has to wait. The first robot gets the cake in the oven 10 minutes before the second. And this means the first robot will take its cake out 10 minutes before the second. And what does the robot do? It turns off the oven.
This is bad but it gets worse. Not only is the second cake not fully cooked but what does the second robot do when it takes its cake out of the oven? It also turns off the oven. But it actually turned the oven back on without realizing it.
The result is not only do you end up with an undercooked cake but you also take the chance of starting a fire.
So what’s all this have to do with programming? And what’s the rule of three that this episode is supposed to explain? I’ll get to that right after this message from our sponsor.
( Message from Sponsor )
Alright, so to start out, the robot did the right thing by trying to clean up after it was done. In C++, this is like a class or struct destructor. Anytime you have a resource that needs special work to clean-up when you’re done, you need a destructor.
If you don’t create a destructor, then your resource won’t ever get cleaned up. This would be like just leaving the oven on all the time when done. That’s no good.
There are many time though when C++ will copy your objects or assign one object to another object. Copying an object is done through a special form of constructor called the copy constructor. And assigning one object to another is done through the assignment operator. Make sure you check out episodes 18 about constructors and 47 about operators for more information.
Just like how the default destructor that the compiler gives you doesn’t know how to cleanup your object, the compiler will also give you a basic copy constructor and assignment operator that blindly copy and assign values from one instance to another. This is sometimes okay.
But when you know that you have resources such as the oven that needs to be turned off that the compiler has no knowledge about, then you really need to provide an implementation for all three of these methods.
Maybe you have a raw pointer that needs to be deleted to free up the memory. Or maybe you have a database connection that needs to be closed. The rule of three says that when you need to manage resources like this, and you find yourself needing to provide either a custom destructor, a copy constructor, or an assignment operator, then you need to implement all three.
If you only provide a destructor that cleans up the resource but not the other two, then whenever a copy of your object is made, it will copy the resource as-is and then both instances will try to cleanup the resource when they get deleted. The same thing happens if you assign one instance to another. Without a custom assignment operator that also knows how to handle the resource, you’ll end up with both instances trying to clean it up when done.
There’s also a couple more related rules called the rule of five and the rule of zero.
The rule of five is actually a more modern version of the rule of three. With later versions of C++, there’s a new concept called move semantics. I haven’t explained this yet so I’ll leave that for a future episode. The important part though is that move semantics require a couple more methods called a move copy constructor and a move assignment operator. That brings the total number of methods that should be implemented together up to five.
Either implement all five (or three if you don’t care about move semantics) or don’t implement any of them. That’s where the rule of zero comes into the picture. You see, sometimes, you might want a class to serve as a base class which means that it should have a virtual destructor. I’ll probably need to explain this too in a future episode. Just know that declaring a virtual destructor when it’s not really needed for resource management is okay but to follow the rule of five, go ahead and declare the other methods too and tell the compiler to go back to their default implementation. This will make it as if you didn’t declare any of them.
Just remember the old saying, one for all and all for one. If you need one of these methods, then make sure you provide all of them.
Do you have a question that you’d like me to answer? Taking the time to ask a question helps not only you but everybody who listens as well.
Just go to takeupcode.com slash contact and you’ll find a form that you can use to submit your question.