The rule of 5 states that if a class has a user-declared destructor, copy constructor, copy assignment constructor, move constructor, or move assignment constructor, then it must have the other 4.
But today it dawned on me: when do you ever need a user-defined destructor, copy constructor, copy assignment constructor, move constructor, or move assignment constructor?
In my understanding, implicit constructors / destructors work just fine for aggregate data structures. However, classes which manage a resource need user-defined constructors / destructors.
However, can't all resource managing classes be converted into an aggregate data structure using a smart pointer?
Example:
// RAII Class which allocates memory on the heap. class ResourceManager { Resource* resource; ResourceManager() {resource = new Resource;} // In this class you need all the destructors/ copy ctor/ move ctor etc... // I haven't written them as they are trivial to implement };
vs
class ResourceManager { std::unique_ptr<Resource> resource; };
Now example 2 behaves exactly the same as example 1, but all the implicit constructors work.
Of course, you can't copy ResourceManager
, but if you want a different behavior, you can use a different smart pointer.
The point is that you don't need user-defined constructors when smart pointers already have those so implicit constructors work.
The only reason I would see to have user-defined constructors would be when:
you can't use smart pointers in some low-level code (I highly doubt this is ever the case).
you are implementing the smart pointers themselves.
However, in normal code I don't see any reason to use user-defined constructors.
Am I missing something here?
The following restrictions apply to constructors and destructors: Constructors and destructors do not have return types nor can they return values. References and pointers cannot be used on constructors and destructors because their addresses cannot be taken. Constructors cannot be declared with the keyword virtual.
The Rule of Five is a modern extension to the Rule of Three. The Rule of Five states that if a type ever needs one of the following, then it must have all five. In addition to copy semantics (Rule of Three), we also have to implement move semantics.
Answer» b. Constructors can take arguments but destructors cannot.
The reason for the rule is that a class which needs any of the three manages some resource (file handles, dynamically allocated memory, etc), and all three are needed to manage that resource consistently.
It discusses 9 rules that every developer should keep in mind while working with constructors, destructors, and finalizers and class hierarchies: Rule #1: Constructors are called in descending order; starting from the root class and stepping down through the tree to reach the last leaf object that you need to instantiate.
This rule also appears in the C++ Core Guidelines as C.20: If you can avoid defining default operations, do . When a base class is intended for polymorphic use, its destructor may have to be declared public and virtual.
Then the constructor is called on every element of the vector, and when the procedure exits, the destructor is called on every element as well. I'll illustrate with src/const5_vector1.cpp :
Unlike Rule of Three, failing to provide move constructor and move assignment is usually not an error, but a missed optimization opportunity. Classes that have custom destructors, copy/move constructors or copy/move assignment operators should deal exclusively with ownership (which follows from the Single Responsibility Principle ).
The full name of the rule is the rule of 3/5/0.
It doesn't say "always provide all five". It says that you have to either provide the three, the five, or none of them.
Indeed, more often than not the smartest move is to not provide any of the five. But you can't do that if you're writing your own container, smart pointer, or a RAII wrapper around some resource.
However, in normal code I don't see any reason to use user-defined constructors.
User provided constructor allows also to maintain some invariant, so orthogonal with rule of 5.
As for example a
struct clampInt { int min; int max; int value; };
doesn't ensure that min < max
. So encapsulate data might provide this guaranty. aggregate doesn't fit for all cases.
when do you ever need a user-defined destructor, copy constructor, copy assignment constructor, move constructor, or move assignment constructor?
Now about rule of 5/3/0.
Indeed rule of 0 should be preferred.
Available smart-pointers (I include container) are for pointers, collections or Lockables. But resources are not necessary pointers (might be handle hidden in an int
, internal hidden static variables (XXX_Init()
/XXX_Close()
)), or might requires more advanced treatment (as for database, an auto commit at end of scope or rollback in case of exceptions) so you have to write your own RAII object.
You might also want to write RAII object which doesn't really own resource, as a TimerLogger
for example (write elapsed time used by a "scope").
Another moment when you generally have to write destructor is for abstract class, as you need virtual destructor (and possible polymorphic copy is done by a virtual clone
).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With