When do we need to define destructors? [duplicate] - c++

This question already has answers here:
What is The Rule of Three?
(8 answers)
Closed 8 years ago.
I read that destructors need to be defined when we have pointer members and when we define a base class, but I am not sure if I completely understand. One of the things I am not sure about is whether or not defining a default constructor is useless or not, since we are always given a default constructor by default. Also, I am not sure if we need to define default constructor to implement the RAII principle (do we just need to put resource allocation in a constructor and not define any destructor?).
class A
{
public:
~Account()
{
delete [] brandname;
delete b;
//do we need to define it?
};
something(){} =0; //virtual function (reason #1: base class)
private:
char *brandname; //c-style string, which is a pointer member (reason #2: has a pointer member)
B* b; //instance of class B, which is a pointer member (reason #2)
vector<B*> vec; //what about this?
}
class B: public A
{
public something()
{
cout << "nothing" << endl;
}
//in all other cases we don't need to define the destructor, nor declare it?
}

The rule of Three and The Rule of Zero
The good ol' way of handling resources was with the Rule of Three (now Rule of Five due to move semantic), but recently another rule is taking over: the Rule of Zero.
The idea, but you should really read the article, is that resource management should be left to other specific classes.
On this regard the standard library provides a nice set of tools like: std::vector, std::string, std::unique_ptr and std::shared_ptr, effectively removing the need for custom destructors, move/copy constructors, move/copy assignment and default constructors.
How to apply it to your code
In your code you have a lot of different resources, and this makes for a great example.
The string
If you notice brandname is effectively a "dynamic string", the standard library not only saves you from C-style string, but automatically manages the memory of the string with std::string.
The dynamically allocated B
The second resource appears to be a dynamically allocated B. If you are dynamically allocating for other reasons other than "I want an optional member" you should definitely use std::unique_ptr that will take care of the resource (deallocating when appropriate) automatically. On the other hand, if you want it to be an optional member you can use std::optional instead.
The collection of Bs
The last resource is just an array of Bs. That is easily managed with an std::vector. The standard library allows you to choose from a variety of different containers for your different needs; Just to mention some of them: std::deque, std::list and std::array.
Conclusion
To add all the suggestions up, you would end up with:
class A {
private:
std::string brandname;
std::unique_ptr<B> b;
std::vector<B> vec;
public:
virtual void something(){} = 0;
};
Which is both safe and readable.

As #nonsensickle points out, the questions is too broad... so I'm gonna try to tackle it with everything I know...
The first reason to re define the destructor would be in The Rule of Three which is on part the item 6 in Scott Meyers Effective C++ but not entirely. The rule of three says that if you re defined the destructor, copy constructor, or copy assignment operations then that means you should rewrite all three of them. The reason is that if you had to rewrite your own version for one, then the compiler defaults will no longer be valid for the rest.
Another example would be the one pointed out by Scott Meyers in Effective C++
When you try to delete a derived class object through a base class pointer and the base class has a non virtual destructor, the results are undefined.
And then he continues
If a class does not contain any virtual functions, that is often an indication that it is not meant to be used as a base class. When a class is not intended to be used as a base class, making the destructor virtual is usually a bad idea.
His conclusion on destructors for virtual is
The bottom line is that gratuitously declaring all destructors virtual is just as wrong as never declaring them virtual. In fact, many people summarize the situation this way: declare a virtual destructor in a class if and only if that class contains at least one virtual function.
And if it is not a Rule Of three case, then maybe you have a pointer member inside your object, and maybe you allocated memory to it inside your object, then, you need to manage that memory in the destructor, this is item 6 on his book
Be sure to check out #Jefffrey's answer on the Rule of Zero

There are precisely two things that necessitate defining a destructor:
When your object gets destructed, you need to perform some action other than destructing all class members.
The vast majority of these actions once was freeing memory, with the RAII principle, these actions have moved into the destructors of the RAII containers, which the compiler takes care of calling. But these actions can be anything, like closing a file, or writing some data to a log, or ... . If you strictly follow the RAII principle, you will write RAII containers for all these other actions, so that only RAII containers have destructors defined.
When you need to destruct objects through a base class pointer.
When you need to do this, you must define the destructor to be virtual within the base class. Otherwise, your derived destructors won't get called, independent of whether they are defined or not, and whether they are virtual or not. Here is an example:
#include <iostream>
class Foo {
public:
~Foo() {
std::cerr << "Foo::~Foo()\n";
};
};
class Bar : public Foo {
public:
~Bar() {
std::cerr << "Bar::~Bar()\n";
};
};
int main() {
Foo* bar = new Bar();
delete bar;
}
This program only prints Foo::~Foo(), the destructor of Bar is not called. There is no warning or error message. Only partially destructed objects, with all the consequences. So make sure you spot this condition yourself when it arises (or make a point to add virtual ~Foo() = default; to each and every nonderived class you define.
If none of these two conditions are met, you don't need to define a destructor, the default constructor will suffice.
Now to your example code:
When your member is a pointer to something (either as a pointer or a reference), the compiler does not know ...
... whether there are other pointers to this object.
... whether the pointer points to one object, or to an array.
Hence, the compiler can't deduce whether, or how to destruct whatever the pointer points to. So the default destructor never destructs anything behind a pointer.
This applies both to brandname and to b. Consequently, you need a destructor, because you need to do the deallocation yourself. Alternatively, you can use RAII containers for them (std::string, and a smart pointer variant).
This reasoning does not apply to vec because this variable directly includes a std::vector<> within the objects. Consequently, the compiler knows that vec must be destructed, which in turn will destruct all its elements (it's a RAII container, after all).

If you dynamically allocate memory, and you want this memory to be deallocated only when the object itself is "terminated", then you need to have a destructor.
The object can be "terminated" in two ways:
If it was statically allocated, then it is "terminated" implicitly (by the compiler).
If it was dynamically allocated, then it is "terminated" explicitly (by calling delete).
When "terminated" explicitly using a pointer of a base-class type, the destructor has to be virtual.

We know that if a destructor is not provided, the compiler will generate one.
This means that anything beyond simple cleanup, such as primitive types, will require a destructor.
In many cases, dynamic allocation or resource acquisition during construction, has a clean up phase. For example, dynamically allocated memory may need to be deleted.
If the class represents a hardware element, the element may need to be turned off, or placed into a safe state.
Containers may need to delete all of their elements.
In summary, if the class acquires resources or requires specialized cleanup (let's say in a determined order), there should be destructor.

Related

Is it possible to store an unique_ptr both in class and vector in C++?

I am trying to hold a unique pointer to both a vector (from a Base class) and a Derivated class object, so later I am able to call a method for all. However I want to call aswell methods from Derivated classes so I need to store the reference for them aswell.
class Foo
{
vector<unique_ptr<Base>> bar;
unique_ptr<Derivated1> bar2;
unique_ptr<Derivated2> bar3;
}
Foo::Foo(){
this->bar2 = make_unique<Derivated1>();
this->bar2->doSomethingDerivated1();
this->bar3 = make_unique<Derivated2>();
this->bar->push_back(move(bar2));
this->bar->push_back(move(bar3));
}
Foo::callForAll() {
for (const auto& foo: this->bar) foo->doSomethingBase();
}
Foo::callForDerivated1() {
this->bar2->doSomethingDerivated1();
}
Is something like this possible? For my understanding this code will most likely fail. Will move place bar2 and bar3 to nullptr? Creating a unique_ptr, store a raw pointer with bar2->get() and then push_back to vector works?
Note: These objects only belong to this class, so unique would make sense.
No. The whole point of unique_ptr is to be unique. This means you cannot copy it.
However, you CAN make copies of the underlying pointer that unique_ptr manages. E.g.
unique_ptr<Foo> foo(new Foo);
Foo* foo_copy = foo.get();
There are a couple things that you need to be careful of when doing this:
Do not delete foo_copy. That is the job of unique_ptr. If you delete foo_copy, then you will have committed the sin of double delete, which has undefined behavior (i.e. the compiler is allowed to generate code that launches a nuclear missile).
Do not use foo_copy after foo has been destroyed because when foo is destroyed, the underlying pointer is deleted (unless foo has relinquished ownership of the underlying pointer, and the underlying pointer hasn't been deleted by some other means yet). This is the sin of use after free, and it also has UB.
Moving is one way for unique_ptr to relinquish ownership. Once you move out of a unique_ptr, it no longer points to anything (that is the whole point of moving; when you walk from A to B, you are no longer at A, because there is only one of you, and you have decided to instead be located at B). I believe that trying to call a method using the -> operator on an empty unique_ptr is also UB.
It seems like what you should do is
vector<unique_ptr<Base>> v;
Derived* ob = v[i].get();
Let me reiterate that this is slightly dangerous and unusual.
Tangent: I find it highly suspicious that you are not using virtual methods; virtual really should be the default (kind of like how double is the default, not float, even though float looks like it is and should be the default). Actually, I find it suspicious that you are using inheritance. Inheritance is one of the most over-rated features of all time. I suspect that this is where your troubles may be originating. For example, the Go language doesn't even have inheritance, yet it does have polymorphism (i.e. different implementations of the same interface).
PS: Please, do not accidentally extinguish the human race by invoking UB.

How to set destructor for a class with unique_ptr<double[]>. the instance is created by make_unique()

I have a class, the member variable is std::unique_ptr<double[]>. Initializing this variable is always done by make_unique<double[]>(size).
But I do not know how to write code for destructor for this member variable
I know std::unique_ptr has a method get_deleter(), but when I look at the docs, it just provide self-defined delete. I do some research about similiar examples. But all of them are about new className(), not make_unique().
the verison of cpp is c++17
My code
class test{
public:
test(int size) : size_{size}, arr_{make_unique<double[]>( size )} {}
~test(){
// how to destroy arr_
}
private:
int size_;
std::unique_ptr<double[]> arr_;
};
I do not know how to start. I know there is a keyword delete, but I think it is not useful in this case.
Do not write a destructor at all.
std::unique_ptr will do the cleanup for you correctly, and indeed that's the main point of using a unique_ptr in the first place.
Whenever possible, follow the Rule Of Zero: Use smart pointers and containers correctly, and don't declare any destructor, copy constructor, move constructor, copy assignment, or move assignment. (See the link for discussion of the main exception, declaring an interface's destructor virtual, and deleting or defaulting the rest.)
I do not know how to write code for destructor for this member variable
Nothing in particular needs to be written in order to destroy any member variable. All member objects as well as base class objects are destroyed by all destructors after the body of the destructor has been executed.
There is no need to do anything in the body of the destructor, and therefore the implicitly generated destructor is sufficient for the class.

Is is safe to `delete this` in a virtual member function?

I know already that some forms of "suicide" are safe (to be considered legal), but, is it specifically safe to perform delete this in a virtual member function?
Note, by "safe", I mean whether the "code" generated by the compiler is able to deal with the construct.
Note, I'm not interested in the pros and cons of doing it, just whether I can consider is safe.
Side question: Does the language standard explicitly or implicitly demand that implementations support any forms of the delete this idiom?
I do not consider this a duplicate of Is delete this allowed?. My question is about whether it is safe to do in a virtual member function.
Here is an outline of what I am pursuing to do
class FooBase {
protected:
virtual void on_idle() { /* no-op by default */ }
};
class Foo : public FooBase {
void on_idle() override final
{
delete this;
}
};
Note that while Foo needs to be heap allocated, other subclasses possibly do not.
Yes, so long as you don't use this afterwards, and neither does anyone else with a pointer to *this, and this was guaranteed to be allocated by new as exactly the type you are deleting it as, or possessing a virtual destructor. (ie, never as a member of another object, in a std::vector, as an automatic storage variable, as a static variable, as a temporary, not new[], not placement new, etc etc etc)
This includes calling non-virtual methods, virtual methods, member access, calling dtors, and a myriad of other things; almost anything other than return; on the next line and somehow every other pointer to *this being cleaned up before you delete this; (or deterministically never being used).
As a general rule, the level of control you have to have over your objects lifetime is so great to make delete this; safe that you can refactor the lifetime management to be external to the class and in a smart resource owner, which maybe maintains its state as a pImpl which it deletes. c++ adores value types, and a type that delete this; can never be treated as a value.
There is nothing in the standard that makes delete this; extra dangerous for virtual objects, other than the higher tendency to inherit.
All types that delete this; should have either a virtual destructor or be final to avoid inheritance issues.

How To Destruct Destructor-less Types Constructed via 'Placement New'

So I have built class with which I intended to use std::aligned_storage to store different types up to 16 bytes in for a 'Variant' class. Theoretically it should be able to store any POD type and common containers such as std::string and std::map.
I went by the code example found here, and it seemed like it was made for exactly what I was looking for: http://en.cppreference.com/w/cpp/types/aligned_storage
My version, basically:
class Variant {
public:
Variant() { /* construct */ }
Variant(std::map<int,int> v) {
new(&m_data) std::map<int,int>(v); // construct std::map<int,int> at &m_data
m_type = TYPE_MAP;
}
~Variant() {
if (m_type == TYPE_MAP) {
// cool, now destruct..?
reinterpret_cast<std::map<int, int>*>(&m_data)->~/*???????????????*/();
}
}
private:
// type of object in m_data
enum Type m_type;
// chunk of space for allocating to
std::aligned_storage<16, std::alignment_of<std::max_align_t>::value>::type m_data;
};
My problem comes with the destruction. As you can see at /*???????????????*/, I'm not sure what to call in place of ~T() in the cppreference.com example:
reinterpret_cast<const T*>(data+pos)->~T(); // I did the same thing except I know what T is, is that a problem is it?
In my mind, I'm doing exactly the same thing, disregarding template anonymity. Problem is, std::map doesn't have any std::map::~map() destructor method, only a std::map::~_Tree, which is clearly not intended for direct use. So, in the cppreference.com example code, what would ~T() be calling if T was an std::map<int,int>, and what is the proper way for me to call the destructor for an object with a known type in std::aligned_storage? Or am I over complicating things and are the clear() methods in these STL containers guaranteed do the equivalent of full destruction's?
Or, is there any simpler way around this? As I've possibly misunderstood something along the way regarding my intended usage of std::aligned_storage.
It sounds like you have read the header file where std::map is defined and think that std::map has no destructor because you could not find the declaration of a destructor.
However, in C++, a type that doesn't have a destructor declared will have a destructor implicitly declared by the compiler. This implicit destructor will call the destructors of bases and non-static members. It sounds like the std::map in your library implementation is a thin layer over _Tree. Therefore, all that needs to be done to destroy the map is to destroy the tree. Therefore, the compiler's default destructor does the trick.
It is allowed to write ->~map() in your case, and it will call the implicitly defined destructor, and the map will be destroyed correctly. You may also use this syntax with scalar types such as int (but not arrays, for some reason).
I'm not sure what to call in place of ~T()
Your type, which is named map:
reinterpret_cast<std::map<int, int>*>(&m_data)->~map();
Which if it makes you feel better you can put in a function template:
template <class T>
void destroy_as(void* p) {
static_cast<T*>(p)->~T();
}
destroy_as<std::map<int, int>>(&m_data);
Problem is, std::map doesn't have any std::map::~map() destructor method
It may be compiler generated, but the type assuredly has a destructor. All types have destructors. Some may be explicitly or impleted deleted, but they exist.
Note that your aligned_storage is too small to store a map, sizeof(std::map) is larger than 16.

std::vector::clear() in constructor and destructor

I encounter many times with code where std::vector::clear() of class member of type std::vector is called in constructor and destructor.
I don't see why it's required:
constructor - the class member of type std::vector is empty by default, so no need to call clear().
destructor - the class member of type std::vector will be destroyed as part of standard destruction of object contnaining it. As part of vector destruction all value objects containied in it will be destroyed (if it heap allocated pointers to memory, they should be deleted "manually"), so again no need to call clear().
Do I miss something?
No, you're not missing anything. I suspect this is (harmless) voodoo programming, sort of like setting a pointer to null after freeing it, or randomly calling repaint/revalidate in GUI code. The programmer remembers that it helped with some sort of bug in the past and now adds it unnecessarily "just in case". Who knows, maybe it'll help. Voodoo.
From the sound of things, the people who wrote that code were the ones who missed something. The only time it would make sense to call clear() in a ctor or dtor would be in the middle of some other code. For example, a ctor might read in some data, process it, then read in more data. In such a case, it's probably faster to use a single container for the data as you read it in, and clear it each time, than to create a new container every iteration.
It's COMPLETELY unnessecary to clear the contents of an stl container in a constructor
It's unnessecary to clear the contents of an stl container in a destructor UNLESS the container contains a pointer. If the pointer has been created using new, it still needs to be deleted first. After that it still will not be nessecary to .clear the container.
Consider this :
#define BOOST_TEST_MODULE StlContainers
#define BOOST_LIB_DIAGNOSTIC
#include <boost/test/unit_test.hpp>
#include <boost/assign.hpp>
#include <boost/assign/list_of.hpp>
#include <boost/assign/std/vector.hpp>
#include <vector>
using namespace boost::assign;
using namespace std;
const vector<int> my_ints_vector = list_of(0)(1)(1)(2)(3)(5)(8)(13)(21)(34);
struct ScopedStruct1
{
ScopedStruct1(const vector<int> & v) : m_v(v) {}
~ScopedStruct1() {}
private :
vector<int> m_v;
};
class A
{
public :
A(int i) : m_i(i) {}
~A() {}
private :
int m_i;
};
struct ScopedStruct2
{
ScopedStruct2() {}
~ScopedStruct2() { for(vector<A*>::iterator it = m_v.begin(); it != m_v.end(); ++it) delete *it; }
vector<A*> m_v;
};
struct ScopedStruct3
{
ScopedStruct3() {}
~ScopedStruct3() { /* no deletion */ }
vector<A*> m_v;
};
BOOST_AUTO_TEST_CASE(StlContainer_storing_something_simple)
{
ScopedStruct1 str(my_ints_vector);
}
BOOST_AUTO_TEST_CASE(StlContainer_storing_pointers_with_delete)
{
ScopedStruct2 str;
for(int i = 0; i < 10; i++)
str.m_v.push_back(new A(i));
}
BOOST_AUTO_TEST_CASE(StlContainer_storing_pointers_without_delete)
{
ScopedStruct3 str;
for(int i = 0; i < 10; i++)
str.m_v.push_back(new A(i));
}
Using boost's unit_test framework I've created 3 test cases. The unit_test framework is greate because it tracks memory leaks.
You'll notice that the 1st and 2nd test case don't generate memory leaks, but the 3rd case does because the vector's contents are not deleted.
Nope, you're right. Unless there is some additional business in the constructor (or constructor of the base classes) that require that, but the chances are very low...
Later edit
In case of destructor, one of the most common mistakes I saw is that some people assume that clear method will also call delete for vectors of pointers (vector), which, of course, it is not the case
The only case I can think of where it would be useful is where the order of destruction matters and the destructor wants to ensure that the objects in the vector are destroyed before something else.
Of course, it is better to structure code to not require that; however, it is a conceivable reason.
Despite what what said so far, there's at least one scenario when an explicit call to clear in the destructor might be necessary.
Imagine the situation when the object being destroyed has several member subobjects, which require some specific order of destruction, i.e. the subobjects somehow depend on each other, and an incorrect order of their destruction will lead to undesirable results. As you probably know, the order of member subobject destruction (as well as member initialization) is determined by the order of members' declararion in class definition. So, one way to achive the proper order of destruction is to arrange the member declarations accordingly. However, firstly, this is not a very good solution maintenance-wise. Secondly, the desired order of destruction might depend on some run-time conditions. Thirdly, the desired order of destruction might contradict the desired oreder of initialization. This all means that it might not be possible (or wise) to command the proper order of destruction by re-arranging the declarations.
A reasonable approach in this case might be to clean-up some critical member subobjects manually, by calling their clean methods or such, until the destruction order dependency "disappears". I would guess, that maybe the code that you saw was trying to resolve to ordering problem by calling clean on a strategically selected vector subobject.
As for calling clean in constructor... I have no idea why anyone would do something like that.
Of course one has to call clear() or resize(0) or the equivalent say (std::_Destroy_range(...) in the destructor before deallocating.
Deallocation is done via allocator::deallocate which does NOT run any destructors. It just frees the memory.
clear() is equivalent to resize(0) which runs destructors on the first size() things in the allocated buffer
NOT just allocated pointers, file handles, held mutexes, all other recoverable resources held by the object. The destructors MUST be run. Before instantiation the template doesn't know that the destructor is trivial. If the destructor is trivial, THEN its gets optimized out AFTER instantiation