Rule of Zero confusion? - c++

So I have been reading about the Rule of Zero.
Simplified version: I do not understand the purpose of this rule. The rule of three and five are sort of "rule of thumbs", but I cannot see the "rule of thumb" or any other specific intentions with this rule.
Detailed version:
Let me quote:
Classes that have custom destructors, copy/move constructors or
copy/move assignment operators should deal exclusively with ownership.
Other classes should not have custom destructors, copy/move
constructors or copy/move assignment operators.
What does this mean? What do they mean by ownership, ownership of what?
They have also showed an example code(I guess it is connected to the introduction):
class rule_of_zero
{
std::string cppstring;
public:
rule_of_zero(const std::string& arg) : cppstring(arg) {}
};
What do they want to show with this, I am really lost on this one.
Also, they are also talking about the scenario when you are dealing with a polymorphic class and the destructor is being declared public and virtual and the fact that this block implicit moves. Therefore, you have to declare them all as defaulted:
class base_of_five_defaults
{
public:
base_of_five_defaults(const base_of_five_defaults&) = default;
base_of_five_defaults(base_of_five_defaults&&) = default;
base_of_five_defaults& operator=(const base_of_five_defaults&) = default;
base_of_five_defaults& operator=(base_of_five_defaults&&) = default;
virtual ~base_of_five_defaults() = default;
};
Does this mean that whenever you have a base class with a destructor that is declared both public and virtual, you really have to declare all the other special member function as defaulted? If so, I do not see why.
I know that this is a lot of confusion in one place.

The Rule of Zero
The rule of zero is another rule of thumb about how to write classes that need to use some resources like memory or other objects. In the example the dynamically allocated memory containing the characters of the string is a resource that has to be managed.
The recommendation is to let specialized classes manage resources, and do only that. In the example, std::string takes care of all the details of managing the allocated memory.
The rule emerged after the introduction of C++11, because the language and the standard library had improved, providing much better facilities to manage dynamically allocated object lifetimes (unique_ptr and shared_ptr). Also the containers now allow in-place construction, removing another reason for dynamic allocation. It should probably be seen as an update to the much older rule of three.
So if you previously would have used new in your constructor to create some member and delete in the destructor, you should now use a unique_ptr to manage the lifetime of the member, getting move construction and move assignment "for free".
Shared pointers can remember the correct destructor to call, so the common need for a virtual destructor goes away for objects that are exclusively managed via shared pointer, even if they are used polymorphically.
So basically a class that can rely on its members to do all the required actions for initialization, moving, copying and destruction should not declare any of the special member functions.
The Rule of Five
As always with C++ things are not always that simple.
As Scott Meyers pointed out, if you do have to add a destructor for whatever reason, the implicit generation of move constructors and move assignment operator are disabled, even if the compiler could generate them.
Then the compiler will happily copy your class all over the place instead of moving it which may not be what you expect. This may for example slow down your program, as more copying has to be performed. The compiler will not warn about this by default.
Therefore he recommends to explicitly specify which of the five special methods you want, in order to avoid surprises due to unrelated changes. He still recommends writing non-resource-management classes such that the defaults generated by the compiler can be used.

Related

Is the Rule of 5 (for constructors and destructors) outdated?

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 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).
The full rule is, as noted, the Rule of 0/3/5; implement 0 of them usually, and if you implement any, implement 3 or 5 of them.
You have to implement the copy/move and destruction operations in a few cases.
Self reference. Sometimes parts of an object refer to other parts of the object. When you copy them, they'll naively refer to the other object you copied from.
Smart pointers. There are reasons to implement more smart pointers.
More generally than smart pointers, resource owning types, like vectors or optional or variants. All of these are vocabulary types that let their users not care about them.
More general than 1, objects whose identity matters. Objects which have external registration, for example, have to reregister the new copy with the register store, and when destroyed have to deregister themselves.
Cases where you have to be careful or fancy due to concurrency. As an example, if you have a mutex_guarded<T> template and you want them to be copyable, default copy doesn't work as the wrapper has a mutex, and mutexes cannot be copied. In other cases, you might need to guarantee the order of some operations, do compare and sets, or even track or record the "native thread" of the object to detect when it has crossed thread boundaries.
Having good encapsulated concepts that already follow the rule of five ensures indeed that you have to worry less about it. That said if you find yourselves in a situation where you have to write some custom logic, it still holds. Some things that come to mind:
Your own smart pointer types
Observers that have to unregister
Wrappers for C-libraries
Next to that, I find that once you have enough composition, it's no longer clear what the behavior of a class will be. Are assignment operators available? Can we copy construct the class? Therefore enforcing the rule of five, even with = default in it, in combination with -Wdefaulted-function-deleted as error helps in understanding the code.
To look closer at your examples:
// 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
};
This code could indeed nicely be converted to:
class ResourceManager {
std::unique_ptr<Resource> resource;
};
However, now imagine:
class ResourceManager {
ResourcePool &pool;
Resource *resource;
ResourceManager(ResourcePool &pool) : pool{pool}, resource{pool.createResource()} {}
~ResourceManager() { pool.destroyResource(resource);
};
Again, this could be done with a unique_ptr if you give it a custom destructor.
Though, if your class now stores a lot of resources, are you willing to pay the extra cost in memory?
What if you first need to take a lock before you can return the resource to the pool to be recycled? Will you take this lock only once and return all resources or 1000 times when you return them 1-by-1?
I think your reasoning is correct, having good smart pointer types makes the rule of 5 less relevant. However, as indicated in this answer, there are always cases to be discovered where you'll need it. So calling it out-dated might be a bit too far, it's a bit like knowing how to iterate using for (auto it = v.begin(); it != v.end(); ++it) instead of for (auto e : v). You no longer use the first variant, up to the point, you need to call 'erase' where this suddenly does become relevant again.
The rule is often misunderstood because it is often found oversimplified.
The simplified version goes like this: if you need to write at least one of (3/5) special methods then you need to write all of the (3/5).
The actual, useful rule: A class that is responsible with manual ownership of a resource should: deal exclusively with managing the ownership/lifetime of the resource; in order to do this correctly it must implement all 3/5 special members. Else (if your class doesn't have manual ownership of a resource) you must leave all special members implicit or defaulted (rule of zero).
The simplified versions uses this rhetoric: if you find yourself in need to write one of the (3/5) then most likely your class manually manages the ownership of a resource so you need to implement all (3/5).
Example 1: if your class manages the acquisition/release of a system resource then it must implement all 3/5.
Example 2: if your class manages the lifetime of a memory region then it must implement all 3/5.
Example 3: in your destructor you do some logging. The reason you write a destructor is not to manage a resource you own so you don't need to write the other special members.
In conclusion: in user code you should follow the rule of zero: don't manual manage resources. Use RAII wrappers that already implement this for you (like smart pointers, standard containers, std::string, etc.)
However if you find yourself in need to manually manage a resource then write a RAII class that is responsible exclusively with the resource lifetime management. This class should implement all (3/5) special members.
A good read on this: https://en.cppreference.com/w/cpp/language/rule_of_three

When to use =default vs =delete

To my understand these semantics are used only with the copy constructor, moving constructor, copy assignment, moving assignment, and the destructor. Using = delete is for prohibiting the use of one of the functions, and that = default is used if you want to be explicit to the compiler on where to use the defaults for these functions.
What are the best practices when using these keywords while making a class? Or rather how do I keep mindful of these when developing a class?
For example, if I don't know whether I'll use one of these functions, is it better to prohibit it with delete or allow it and use default?
Good question.
Also important: Where to use = default and = delete.
I have somewhat controversial advice on this. It contradicts what we all learned (including myself) for C++98/03.
Start your class declaration with your data members:
class MyClass
{
std::unique_ptr<OtherClass> ptr_;
std::string name_;
std::vector<double> data_;
// ...
};
Then, as close as is practical, list all of the six special members that you want to explicitly declare, and in a predictable order (and don't list the ones you want the compiler to handle). The order I prefer is:
destructor // this tells me the very most important things about this class.
default constructor
copy constructor // I like to see my copy members together
copy assignment operator
move constructor // I like to see my move members together
move assignment operator
The reason for this order is:
Whatever special members you default, the reader is more likely to understand what the defaults do if they know what the data members are.
By listing the special members in a consistent place near the top, and in a consistent order, the reader is more likely to quickly realize which special members are not explicitly declared &dash; and thus either implicitly declared, or not do not exist at all.
Typically both copy members (constructor and assignment) are similar. Either both will
be implicitly defaulted or deleted, explicitly defaulted or deleted, or explicitly supplied. It is nice to confirm this in two lines of code right next to each other.
Typically both move members (constructor and assignment) are similar...
For example:
class MyClass
{
std::unique_ptr<OtherClass> ptr_;
std::string name_;
std::vector<double> data_;
public:
MyClass() = default;
MyClass(const MyClass& other);
MyClass& operator=(const MyClass& other);
MyClass(MyClass&&) = default;
MyClass& operator=(MyClass&&) = default;
// Other constructors...
// Other public member functions
// friend functions
// friend types
// private member functions
// ...
};
Knowing the convention, one can quickly see, without having to examine the entire class declaration that ~MyClass() is implicitly defaulted, and with the data members nearby, it is easy to see what that compiler-declared and supplied destructor does.
Next we can see that MyClass has an explicitly defaulted default constructor, and with the data members declared nearby, it is easy to see what that compiler-supplied default constructor does. It is also easy to see why the default constructor has been explicitly declared: Because we need a user-defined copy constructor, and that would inhibit a compiler-supplied default constructor if not explicitly defaulted.
Next we see that there is a user-supplied copy constructor and copy assignment operator. Why? Well, with the data members nearby, it is easy to speculate that perhaps a deep-copy of the unique_ptr ptr_ is needed. We can't know that for sure of course without inspecting the definition of the copy members. But even without having those definitions handy, we are already pretty well informed.
With user-declared copy members, move members would be implicitly not declared if we did nothing. But here we easily see (because everything is predictably grouped and ordered at the top of the MyClass declaration) that we have explicitly defaulted move members. And again, because the data members are nearby, we can immediately see what these compiler-supplied move members will do.
In summary, we don't yet have a clue exactly what MyClass does and what role it will play in this program. However even lacking that knowledge, we already know a great deal about MyClass.
We know MyClass:
Holds a uniquely owning pointer to some (probably polymorphic) OtherClass.
Holds a string serving as a name.
Holds a bunch of doubles severing as some kind of data.
Will properly destruct itself without leaking anything.
Will default construct itself with a null ptr_, empty name_ and data_.
Will copy itself, not positive exactly how, but there is a likely algorithm we can easily check elsewhere.
Will efficiently (and correctly) move itself by moving each of the three data members.
That's a lot to know within 10 or so lines of code. And we didn't have to go hunting through hundreds of lines of code that I'm sure are needed for a proper implementation of MyClass to learn all this: because it was all at the top and in a predictable order.
One might want to tweak this recipe say to place nested types prior to the data members so that the data members can be declared in terms of the nested types. However the spirit of this recommendation is to declare the private data members, and special members, both as close to the top as practical, and as close to each other as practical. This runs contrary to advice given in the past (probably even by myself), that private data members are an implementation detail, not important enough to be at the top of the class declaration.
But in hindsight (hindsight is always 20/20), private data members, even though being inaccessible to distant code (which is a good thing) do dictate and describe the fundamental behaviors of a type when any of its special members are compiler-supplied.
And knowing what the special members of a class do, is one of the most important aspects of understanding any type.
Is it destructible?
Is it default constructible?
Is it copyable?
Is it movable?
Does it have value semantics or reference semantics?
Every type has answers to these questions, and it is best to get these questions & answers out of the way ASAP. Then you can more easily concentrate on what makes this type different from every other type.
Also, using =default instead of a hand-rolled one keeps the POD nature of the class, as it is described here in detail: Default constructors and POD
You often see = default when you are trying to maintain the rule of 5 to ensure the special member functions behave as you intend them to, and so that the reader of the class can see that you did consider how you wanted that function to behave.
You can use = delete if you intent to make something non-copyable or non-movable, for example. Though I have also seen people delete an inherited function if they do not want that specific derived class to have that function, though I'm not a huge fan of that since it tends to point towards poor architecture/design.

Can we say bye to copy constructors?

Copy constructors were traditionally ubiquitous in C++ programs. However, I'm doubting whether there's a good reason to that since C++11.
Even when the program logic didn't need copying objects, copy constructors (usu. default) were often included for the sole purpose of object reallocation. Without a copy constructor, you couldn't store objects in a std::vector or even return an object from a function.
However, since C++11, move constructors have been responsible for object reallocation.
Another use case for copy constructors was, simply, making clones of objects. However, I'm quite convinced that a .copy() or .clone() method is better suited for that role than a copy constructor because...
Copying objects isn't really commonplace. Certainly it's sometimes necessary for an object's interface to contain a "make a duplicate of yourself" method, but only sometimes. And when it is the case, explicit is better than implicit.
Sometimes an object could expose several different .copy()-like methods, because in different contexts the copy might need to be created differently (e.g. shallower or deeper).
In some contexts, we'd want the .copy() methods to do non-trivial things related to program logic (increment some counter, or perhaps generate a new unique name for the copy). I wouldn't accept any code that has non-obvious logic in a copy constructor.
Last but not least, a .copy() method can be virtual if needed, allowing to solve the problem of slicing.
The only cases where I'd actually want to use a copy constructor are:
RAII handles of copiable resources (quite obviously)
Structures that are intended to be used like built-in types, like math vectors or matrices -
simply because they are copied often and vec3 b = a.copy() is too verbose.
Side note: I've considered the fact that copy constructor is needed for CAS, but CAS is needed for operator=(const T&) which I consider redundant basing on the exact same reasoning;
.copy() + operator=(T&&) = default would be preferred if you really need this.)
For me, that's quite enough incentive to use T(const T&) = delete everywhere by default and provide a .copy() method when needed. (Perhaps also a private T(const T&) = default just to be able to write copy() or virtual copy() without boilerplate.)
Q: Is the above reasoning correct or am I missing any good reasons why logic objects actually need or somehow benefit from copy constructors?
Specifically, am I correct in that move constructors took over the responsibility of object reallocation in C++11 completely? I'm using "reallocation" informally for all the situations when an object needs to be moved someplace else in the memory without altering its state.
The problem is what is the word "object" referring to.
If objects are the resources that variables refers to (like in java or in C++ through pointers, using classical OOP paradigms) every "copy between variables" is a "sharing", and if single ownership is imposed, "sharing" becomes "moving".
If objects are the variables themselves, since each variables has to have its own history, you cannot "move" if you cannot / don't want to impose the destruction of a value in favor of another.
Cosider for example std::strings:
std::string a="Aa";
std::string b=a;
...
b = "Bb";
Do you expect the value of a to change, or that code to don't compile? If not, then copy is needed.
Now consider this:
std::string a="Aa";
std::string b=std::move(a);
...
b = "Bb";
Now a is left empty, since its value (better, the dynamic memory that contains it) had been "moved" to b. The value of b is then chaged, and the old "Aa" discarded.
In essence, move works only if explicitly called or if the right argument is "temporary", like in
a = b+c;
where the resource hold by the return of operator+ is clearly not needed after the assignment, hence moving it to a, rather than copy it in another a's held place and delete it is more effective.
Move and copy are two different things. Move is not "THE replacement for copy". It an more efficient way to avoid copy only in all the cases when an object is not required to generate a clone of itself.
Short anwer
Is the above reasoning correct or am I missing any good reasons why logic objects actually need or somehow benefit from copy constructors?
Automatically generated copy constructors are a great benefit in separating resource management from program logic; classes implementing logic do not need to worry about allocating, freeing or copying resources at all.
In my opinion, any replacement would need to do the same, and doing that for named functions feels a bit weird.
Long answer
When considering copy semantics, it's useful to divide types into four categories:
Primitive types, with semantics defined by the language;
Resource management (or RAII) types, with special requirements;
Aggregate types, which simply copy each member;
Polymorphic types.
Primitive types are what they are, so they are beyond the scope of the question; I'm assuming that a radical change to the language, breaking decades of legacy code, won't happen. Polymorphic types can't be copied (while maintaining the dynamic type) without user-defined virtual functions or RTTI shenanigans, so they are also beyond the scope of the question.
So the proposal is: mandate that RAII and aggregate types implement a named function, rather than a copy constructor, if they should be copied.
This makes little difference to RAII types; they just need to declare a differently-named copy function, and users just need to be slightly more verbose.
However, in the current world, aggregate types do not need to declare an explicit copy constructor at all; one will be generated automatically to copy all the members, or deleted if any are uncopyable. This ensures that, as long as all the member types are correctly copyable, so is the aggregate.
In your world, there are two possibilities:
Either the language knows about your copy-function, and can automatically generate one (perhaps only if explicitly requested, i.e. T copy() = default;, since you want explicitness). In my opinion, automatically generating named functions based on the same named function in other types feels more like magic than the current scheme of generating "language elements" (constructors and operator overloads), but perhaps that's just my prejudice speaking.
Or it's left to the user to correctly implement copying semantics for aggregates. This is error-prone (since you could add a member and forget to update the function), and breaks the current clean separation between resource management and program logic.
And to address the points you make in favour:
Copying (non-polymorphic) objects is commonplace, although as you say it's less common now that they can be moved when possible. It's just your opinion that "explicit is better" or that T a(b); is less explicit than T a(b.copy());
Agreed, if an object doesn't have clearly defined copy semantics, then it should have named functions to cover whatever options it offers. I don't see how that affects how normal objects should be copied.
I've no idea why you think that a copy constructor shouldn't be allowed to do things that a named function could, as long as they are part of the defined copy semantics. You argue that copy constructors shouldn't be used because of artificial restrictions that you place on them yourself.
Copying polymorphic objects is an entirely different kettle of fish. Forcing all types to use named functions just because polymorphic ones must won't give the consistency you seem to be arguing for, since the return types would have to be different. Polymorphic copies will need to be dynamically allocated and returned by pointer; non-polymorphic copies should be returned by value. In my opinion, there is little value in making these different operations look similar without being interchangable.
One case where copy constructors come in useful is when implementing the strong exception guarantees.
To illustrate the point, let's consider the resize function of std::vector. The function might be implemented roughly as follows:
void std::vector::resize(std::size_t n)
{
if (n > capacity())
{
T *newData = new T [n];
for (std::size_t i = 0; i < capacity(); i++)
newData[i] = std::move(m_data[i]);
delete[] m_data;
m_data = newData;
}
else
{ /* ... */ }
}
If the resize function were to have a strong exception guarantee we need to ensure that, if an exception is thrown, the state of the std::vector before the resize() call is preserved.
If T has no move constructor, then we will default to the copy constructor. In this case, if the copy constructor throws an exception, we can still provide strong exception guarantee: we simply delete the newData array and no harm to the std::vector has been done.
However, if we were using the move constructor of T and it threw an exception, then we have a bunch of Ts that were moved into the newData array. Rolling this operation back isn't straight-forward: if we try to move them back into the m_data array the move constructor of T may throw an exception again!
To resolve this issue we have the std::move_if_noexcept function. This function will use the move constructor of T if it is marked as noexcept, otherwise the copy constructor will be used. This allows us to implement std::vector::resize in such a way as to provide a strong exception guarantee.
For completeness, I should mention that C++11 std::vector::resize does not provide a strong exception guarantee in all cases. According to www.cplusplus.com we have the the follow guarantees:
If n is less than or equal to the size of the container, the function never throws exceptions (no-throw guarantee).
If n is greater and a reallocation happens, there are no changes in the container in case of exception (strong guarantee) if the type of the elements is either copyable or no-throw moveable.
Otherwise, if an exception is thrown, the container is left with a valid state (basic guarantee).
Here's the thing. Moving is the new default- the new minimum requirement. But copying is still often a useful and convenient operation.
Nobody should bend over backwards to offer a copy constructor anymore. But it is still useful for your users to have copyability if you can offer it simply.
I would not ditch copy constructors any time soon, but I admit that for my own types, I only add them when it becomes clear I need them- not immediately. So far this is very, very few types.

Destructor in class

I have created a class, and according to the textbook Accelerated C++ by Andrew Koenig and Barbara E. Moo,
The work of the destructor is to do any cleanup that should be done whenever an object goes away. Typically this cleanup involves releasing resources, such as memory, that the constructor has allocated.
I am trying to write a destructor, and I'm getting confused by all the code floating out there. Sometimes a simple deconstructor like this is used ~MyIntArray() {} and sometimes there are things between the {}.
What is the rule behind putting things between the curly brackets or not? Is it just containers e.g. lists, arrays, vectors, pointers that need to be placed between the curly brackets (these are the things I see in code examples out there).
edit: this is my class in case that's needed
class msgInfo
{
public:
msgInfo();
msgInfo(int, int, int, std::string, std::list<int>);
private:
int source_id;
int dest_id;
int priority;
std::string payload;
std::list<int> nodePath;
};
Rule 1:
Rule of three in C++03 or rule of five in C++11.
If your class needs a user defined copy constructor or a copy assignment operator then it most likely needs a user defined destructor.
When do you need either of these 3?
When your class has dynamically allocated pointer members and you need to maintain lifetime of each separate from that of another instance member. For e.g: char * member.
When you manage resources. For e.g: Open file handles, mutex locks etc.
Rule 2:
If your class is intended to be used for derivation and you need polymorphic deletion of objects then you must mark the destructor in Base class as virtual.
Well, if you allocated resources dynamically (new etc..) then in the destructor you'd want to release them (delete), in your case, since all of your members are not allocated dynamically, your destructor can be empty ( or non existent).
Another note worth mentioning is, if you do end up implementing the destructor, and you plan on someone inherting your class, you should make it virtual.
It is a good programming practice to provide a destructor in your C++ program even if there is no explicit need for one. In your code you might not have any dynamic memory allocation, so the destructor provided is simply ~MyIntArray() {} without any code inside.
Please also read the Wikipedia article on Rule of Three in C++.
http://en.wikipedia.org/wiki/Rule_of_three_(C%2B%2B_programming)
If you don't provide a destructor, the compiler will provide one for you. This automatically-generator destructor will correctly call the destructors of all of your class's data members, such as payload etc.
If you don't need to do anything beyond that then you don't need to explicitly provide a destructor. Alternatively, an empty one would work equally well.
If, on the other hand, your constructor allocates some resources (for example, connects to a database), then typically you'd need to put some code in your destructor to release that resource (e.g. disconnect from the database). This is the standard C++ idiom used to prevent resource leaks.
Your class does not have any resources that need to be dealt with in the destructor: each type is either built-in (`int) or handles its own resources (std::string,std::list`). So you do not need to implement your own destructor. The compiler will provide one that is equivalent to the empty braces one.
You would need to implement your own if your class had resources that need dealing with: dynamically allocated objects, handles or connections to sockets, databases, reference counts, etc.
One situation where it might make sense to implement an empty destructor is when you have a class which is intended to be derived from and used polymorphically. In this case, a virtual destructor is needed (there are many SO posts about that), and it is common practice to provide an empty implementation so that derived types to not have to implement it themselves when there are no resources to deal with.
virtual ~Foo() {}
A class like this doesn't need a non-trivial destructor (one, with "things between the curly brackets").
In a destructor, you must release resources, you have "manually" allocated. For example:
if you have new/new[] in the constructor and you need to free this memory in the destructor
if you have opened a file in the constructor, close it in the destructor
if you have locked a mutex in the constructor, unlock it in the destructor
Things like this.
Also, you may need some additional logic to be implemented, when an object is being destructed. Depends on what you're trying to do.
A definition of a destructor should, as far as I know or am concerned, should always look like this:
~msgInfo() { /* free stuff */ }
A constructor on the other hand may look like this:
msgInfo(): m_var1(0), m_var2("Initialize") { /* further initialization */ }
Where what comes between : and { is member variable initialization.
In the destructor you should deallocate anything which was dynamically allocated elsewhere in the class, hence the : notation is no good, since you probably need to do delete on the objects.
In your code you might not have any dynamic memory allocation,so you don't need to provide a destructor.

Under what circumstances must I provide, assignment operator, copy constructor and destructor for my C++ class? [duplicate]

This question already has answers here:
What is The Rule of Three?
(8 answers)
Closed 10 years ago.
Say I've got a class where the sole data member is something like std::string or std::vector. Do I need to provide a Copy Constructor, Destructor and Assignment Operator?
In case your class contains only vector/string objects as its data members, you don't need to implement these. The C++ STL classes (like vector, string) have their own copy ctor, overloaded assignment operator and destructor.
But in case if your class allocates memory dynamically in the constructor then a naive shallow copy will lead to trouble. In that case you'll have to implement copy ctor, overloaded assignment operator and destructor.
The usual rule of thumb says: if you need one of them, then you need them all.
Not all classes need them, though. If you class holds no resources (memory, most notably), you'll be fine without them. For example, a class with a single string or vector constituent doesn't really need them - unless you need some special copying behavior (the default will just copy over the members).
The default copy constructor will copy the vector if it is declared by value. Beware if you stored pointers in your vector, in such a case, you need to provide specific behaviour for copy/assignement/destruction to avoid memory leaks or multiple delete.
I can think of a few cases when you need to write your own Big Three. All standard containers know how to copy and destroy themselves, so you don't necessarily need to write them. Here's how to know when you do:
Does my class own any resources?
The default copy semantics for pointers is to copy the value of the pointer, not what it points to. If you need to deep copy something, even if it's stored inside a standard container, you need to write your own copy constructor and assignment operator. You also need to write your own destructor to properly free those resources.
Might someone inherit from my class?
Base classes need a destructor. Herb Sutter recommends making them either public and virtual (most common case) or protected and non-virtual, depending on what you want to do with them. The compiler-generated destructor is public and non-virtual, so you'll have to write your own, even if it doesn't have any code in it. (Note: this doesn't imply you have to write a copy constructor or assignment operator.)
Should I prevent a user from copying objects of my class?
If you don't want the user to copy your objects (maybe that's too expensive), you need to declare the copy constructor and assignment operators either protected or private. You don't have to implement them unless you need them. (Note: this doesn't imply you have to write a destructor.)
Bottom line:
The most important thing is to understand what the compiler-generated copy constructor, assignment operator, and destructor will do. You don't need to be afraid of them, but you need to think about them and decide if their behavior is appropriate for your class.
No but there are a number of reasons why you shouldn't allow the compiler to auto generate these functions.
In my experience it is always best to define them yourself, and to get into the habit of making sure that they are maintained when you change the class. Firstly you may well want to put a breakpoint on when a particular ctor or dtor is called. Also not defining them can result in code bloat as the compiler will generate inline calls to member ctor and dtor (Scott Meyers has a section on this).
Also you sometimes want to disallow the default copy ctors and assignments. For example I have an application that stores and manipulates very large blocks of data. We routinely have the equivalent of an STL vector holding millions of 3D points and it would be a disaster if we allowed those containers to be copy constructed. So the ctor and assignment operators are declared private and not defined. That way if anyone writes
class myClass {
void doSomething(const bigDataContainer data); // not should be passed by reference
}
then they'll get a compiler error. Our experience is that an explicit become() or clone() method is far less error prone.
So all in all there are many reason to avoid auto generated compiler functions.
those container will need a "copy constructible" element, and if you don't supply the copy constructor, it will call default copy constructor of your class by deducing from your class members (shallow copy).
easy explanation about default copy constructor is here : http://www.fredosaurus.com/notes-cpp/oop-condestructors/copyconstructors.html
it is so with destructor, the container need to have access to your destructor or your default class destructor if you don't provides one (ie. it will not work if you declare your destructor as private )
you need to provide them if you need them. or possible users of your classes. destructor is always a must, and copy constructors and assignment operator are automatically created by compiler. (MSVC at least)
When ever you have a class that requires deep copies, you should define them.
Specifically, any class which contains pointers or references should contain them such as:
class foo {
private:
int a,b;
bar *c;
}
Subjectively, I would say always define them, as the default behavior provided by the compiler generated version may not be what you expect / want.
Not for strings or vectors, since the trivial constructors / destructors etc, will do fine.
If your class has pointers to other data and need deep copies, or if your class holds a resource that has to be deallocated or has to be copied in a special way.