Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Virtual destructor with virtual members in C++11

In these slides about C++11/14 standard, on slide 15, the author writes that "many classic coding rules [are] no longer applicable" in C++11. He proposes a list of three examples, and I agree with the Rule of Three and the memory management.

However his second example is "Virtual destructor with virtual members" (just that). What does it mean? I know one must declare as virtual the base class destructor in order to call the right destructor if we have something like

Base *b = new Derived; ... delete b; 

This is well explained here: When to use virtual destructors?

But is it useless now in C++11 to declare virtual your destructor if you have virtual members?

like image 376
Florian Richoux Avatar asked Apr 02 '14 09:04

Florian Richoux


People also ask

Can a destructor be virtual?

Yes, it is possible to have a pure virtual destructor. Pure virtual destructors are legal in standard C++ and one of the most important things to remember is that if a class contains a pure virtual destructor, it must provide a function body for the pure virtual destructor.

What is a virtual destructor explain with an example?

A virtual destructor is used to free up the memory space allocated by the derived class object or instance while deleting instances of the derived class using a base class pointer object.

Which has virtual members does not have a virtual destructor?

A C++ class containing virtual member functions has a non-virtual destructor. Since this class has virtual member functions, it will be used as a base class. The use of non-virtual destructors in base classes is dangerous because it can cause objects to be torn down incorrectly.

What happens if virtual Desctructor is not used in C++?

If your base class destructor is NOT virtual then only base class object will get deleted(because pointer is of base class "Base *myObj"). So there will be memory leak for derived object.


2 Answers

As the author of the slides I'll try to clarify.

If you write code explicitly allocating a Derived instance with new and destroying it with delete using a base class pointer then you need to define a virtual destructor, otherwise you end up with incompletely destroying the Derived instance. However, I recommend to abstain from new and delete completely and use exclusively shared_ptr for referring to heap-allocated polymorphic objects, like

shared_ptr<Base> pb=make_shared<Derived>(); 

This way, the shared pointer keeps track of the original destructor to be used, even if shared_ptr<Base> is used to represent it. Once, the last referring shared_ptr goes out of scope or is reset, ~Derived() will be called and the memory released. Therefore, you don't need to make ~Base() virtual.

unique_ptr<Base> and make_unique<Derived> do not provide this feature, because they don't provide the mechanics of shared_ptr with respect to the deleter, because unique pointer is much simpler and aims for the lowest overhead and thus is not storing the extra function pointer needed for the deleter. With unique_ptr the deleter function is part of the type and thus a uniqe_ptr with a deleter referring to ~Derived would not be compatible with a unique_ptr<Base> using the default deleter, which would be wrong for a derived instance anyway, if ~Base wasn't virtual.

The individual suggestions I make, are meant to be easy to follow and followed all together. They try to produce simpler code, by letting all resource management be done by library components and the compiler generated code.

Defining a (virtual) destructor in a class, will prohibit a compiler-provided move constructor/assignment operator and might prohibit also a compiler provided copy constructor/assignment operator in future versions of C++. Resurrecting them has become easy with =default, but still looks like a lot of boilerplate code. And the best code is the code you don't have to write, because it can not be wrong (I know there are still exceptions to that rule).

To summarize "Don't define a (virtual) destructor" as a corollary to my "Rule of Zero":

Whenever you design a polymorphic (OO) class hierarchy in modern C++ and want/need to allocate its instances on the heap and access them through a base class pointer use make_shared<Derived>() to instantiate them and shared_ptr<Base> to keep them around. This allows you to keep the "Rule of Zero".

This doesn't mean you must allocate all polymorphic objects on the heap. For example, defining a function taking a (Base&) as parameter, can be called with a local Derived variable without problems and will behave polymorphic, with respect to virtual member functions of Base.

In my opinion dynamic OO polymorphism is heavily overused in many systems. We shouldn't program like Java, when we use C++, unless we have a problem, where dynamic polymorphism with heap allocated objects is the right solution.

like image 157
PeterSom Avatar answered Sep 21 '22 22:09

PeterSom


I think that this is to do with the "rule of zero" mentioned elsewhere in the presentation.

If you only have automatic member variables (i.e. use shared_ptr or unique_ptr for members that would otherwise be raw pointers) then you don't need to write your own copy or move constructors, or assignment operators -- the compiler-provided defaults will be optimal. With in-class initialisation, you don't need a default constructor either. And finally, you don't need to write a destructor at all, virtual or not.

like image 24
Tristan Brindle Avatar answered Sep 22 '22 22:09

Tristan Brindle