Everyone knows that the desructor of base class usually has to be virtual. But what is about the destructor of derived class? In C++11 we have keyword "override" and ability to use the default destructor explicitly.
struct Parent
{
std::string a;
virtual ~Parent()
{
}
};
struct Child: public Parent
{
std::string b;
~Child() override = default;
};
Is it correct to use both keywords "override" and "=default" in the destructor of Child class? Will compiler generate correct virtual destructor in this case?
If yes, then can we think that it is good coding style, and we should always declare destructors of derived classes this way to ensure that base class destructors are virtual?
Is it correct to use both keywords "override" and "=default" in the destructor of Child class? Will compiler generate correct virtual destructor in this case?
Yes, it is correct. On any sane compiler, if the code compiles without error, this destructor definition will be a no-op: its absence must not change the behavior of the code.
can we think that it is good coding style
It's a matter of preference. To me, it only makes sense if the base class type is templated: it will enforce a requirement on the base class to have a virtual destructor, then. Otherwise, when the base type is fixed, I'd consider such code to be noise. It's not as if the base class will magically change. But if you have deadheaded teammates that like to change things without checking the code that depends on what they may be possibly breaking, it's best to leave the destructor definition in - as an extra layer of protection.
override is nothing more than a safety net. Destructor of the child class will always be virtual if base class destructor is virtual, no matter how it is declared - or not declared at all (i.e. using implicitly declared one).
According to the CppCoreGuidelines C.128 the destructor of the derived class should not be declared virtual or override.
If a base class destructor is declared virtual, one should avoid declaring derived class destructors virtual or override. Some code base and tools might insist on override for destructors, but that is not the recommendation of these guidelines.
UPDATE: To answer the question why we have a special case for destructors.
Method overriding is a language feature that allows a subclass or child class to provide a specific
implementation of a method that is already provided by one of its superclasses or parent classes. The implementation in the subclass overrides (replaces) the implementation in the superclass by providing a method that has same name, same parameters or signature, and same return type as the method in the parent class.
In other words, when you call an overridden method only the last implementation of that method (in the class hierarchy) is actually executed while all the destructors (from the last child to the root parent) must be called to properly release all the resources owned by the object.
Thus we don't really replace (override) the destructor, we add additional one into the chain of object destructors.
UPDATE: This CppCoreGuidelines C.128 rule was changed (by 1448, 1446 issues) in an effort to simplify already exhaustive list of exceptions. So the general rule can be summarized as:
For class users, all virtual functions including destructors are equally polymorphic.
Marking destructors override on state-owning subclasses is textbook hygiene that you should all be doing by routine (ref.).
There is (at least) one reason for using override here -- you ensure that the base class's destructor is always virtual. It will be a compilation error if the derived class's destructor believes it is overriding something, but there is nothing to override. It also gives you a convenient place to leave generated documentation, if you're doing that.
On the other hand, I can think of two reasons not to do this:
It's a little weird and backwards for the derived class to enforce behavior from the base class.
If you define a destuctor in the header (or if you make it inline), you do introduce the possibility for odd compilation errors. Let's say your class looks like this:
struct derived {
struct impl;
std::unique_ptr<derived::impl> m_impl;
~derived() override = default;
};
You will likely get a compiler error because the destructor (which is inline with the class here) will be looking for the destructor for the incomplete class, derived::impl.
This is my round-about way of saying that every line of code can become a liability, and perhaps it's best to just skip something if it functionally does nothing. If you really really need to enforce a virtual destructor in the base class from the parent class, someone suggested using static_assert in concert with std::has_virtual_destructor, which will produce more consistent results, IMHO.
I think "override" is kind of misleading on destructor.
When you override virtual function, you replace it.
The destructors are chained, so you can't override destructor literally
The CPP Reference says that override makes sure that the function is virtual and that it indeed overrides a virtual function. So the override keyword would make sure that the destructor is virtual.
If you specify override but not = default, then you will get a linker error.
You do not need to do anything. Leaving the Child dtor undefined works just fine:
#include <iostream>
struct Notify {
~Notify() { std::cout << "dtor" << std::endl; }
};
struct Parent {
std::string a;
virtual ~Parent() {}
};
struct Child : public Parent {
std::string b;
Notify n;
};
int main(int argc, char **argv) {
Parent *p = new Child();
delete p;
}
That will output dtor. If you remove the virtual at Parent::~Parent, though, it will not output anything because that is undefined behavior, as pointed out in the comments.
Good style would be to not mention Child::~Child at all. If you cannot trust that the base class declared it virtual, then your suggestion with override and = default will work; I would hope that there are better ways to ensure that instead of littering your code with those destructor declarations.
Though destructors are not inherited there is clear written in the Standard that virtual destructors of derived classes override destructors of base classes.
From the C++ Standard (10.3 Virtual functions)
6 Even though destructors are not inherited, a destructor in a derived
class overrides a base class destructor declared virtual; see 12.4 and
12.5.
On the other hand there is also written (9.2 Class member)
8 A virt-specifier-seq shall contain at most one of each virt-specifier.
A virt-specifier-seq shall appear only in the declaration of a
virtual member function (10.3).
Though destructors are called like special member functions nevertheless they are also member functions.
I am sure the C++ Standard should be edited such a way that it was unambiguous whether a destructor may have virt-specifier override. At present it is not clear.
Related
Yes I have seen a lots of posts about using the keywords virtual and override for destructors in C++. I also think I understand the usage:
if a base class has a virtual destructor and a derived class overrides it, if the signatures are different, the program will not compile due to override.
However I am wondering - or I have seen it also several times in someones code, that it is used like this:
class Base
{
public:
~Base();
};
class Derived : public Base
{
public:
~Derived() override;
};
Does this override on a destructor of a non-virtual function in the base class actually has any impact on the program / compiling or anything? Or is it just used wrongly in that case?
You code doesn't compile because Base::~Base is not virtual.
Oh ok, maybe I have overseen this: if the Base class derives from
another class, say SuperBase class - which has a virtual destructor,
then the destructor of Base would be virtual without using the keyword
right?
Correct. If a method is virtual in a base class, then the method in the child class with the same name and same signature will also be implicitly virtual. virtual keyword can be omitted.
A good practice is to always use the keyword override on a method intended to override. This has two big advantages: it makes it clear to a human reader that the method is virtual and overrides and it avoid some bugs where the method is indented to override, but it silently doesn't (e.g. const mismatch).
Destructors are a special case in the sense that that they override a parent virtual destructor, even if they have different names.
This question already has answers here:
When to use virtual destructors?
(20 answers)
Closed 3 years ago.
Based on what I found here and on other links on stackoverflow, we should always define a virtual destructor in the base class if we plan to use it polymorphically. I want to know if there is an exception to this rule.
I have seen production code that does not define virtual destructor for the pure abstract base classes and in one of cppcon 2014 video Accept no visitor, around 10:06 the BoolExp struct defined is a pure abstract class and has no virtual destructor.
So for a pure abstract class defined like this
class Base {
public:
virtual foo() = 0;
virtual bar() = 0;
}
My question is it absolutely must that we define a virtual destructor for "Base" class, even though it does have any data members? Are there any exceptions to the virtual destructor rule?
Thanks in advance.
Best,
RG
My question is it absolutely must that we define a virtual destructor for "Base" class, even though it does have any data members?
It depends. If you have a case like
base * foo = new child(stuff);
// doing stuff
delete foo;
then you absolutely must have a virtual destructor. Without it you'll never destroy the child part.
If you have a case like
child * foo = new child(stuff);
// doing stuff
delete foo;
Then you do not need a virtual destructor, as child's will be called.
So the rule is if you delete polymorphically, you need a polymorphic (virtual) destructor, if not, then you don't
The exception to the rule is if you never delete an object through a pointer to the base class. In that case the base class destructor does not need to be virtual.
But if you ever delete an object via a base class pointer, then the base class destructor must be virtual or your program has Undefined Behaviour.
My question is it absolutely must that we define a virtual destructor for "Base" class, even though it does have any data members?
Stricktly speaking, No.
However, whether the base class has any member variables is not relevant. If the destructor gets called using a pointer to the base class, your code has undefined behavior regardless of whether the base class has any member variables or not.
Are there any exceptions to the virtual destructor rule?
If you are able to manage lifetimes of derived classes in such a way that the call to delete the objects is done via derived class pointers, you don't invoke undefined behavior and your code will be well behaved, assuming everything else is in your code base is in good order.
we should always define a virtual destructor in the base class if we plan to use it polymorphically.
You should always define a virtual destructor in a base classs if we plan to delete it polymorphically through that base class.
Now, one problem is that "I don't intend to" isn't safe; you should make it impossible.
Make the destructor virtual (and empty), or make it protected (and empty). A protected destructor makes polymorphic deletion unlikely (it can be bypassed, but only through pretty insane means).
Barring that, you have to be careful. This is one of the reasons why inheriting from (say) std vector is a thing to be wary of.
is it absolutely must that we define a virtual destructor for "Base" class, even though it does have any data members? Are there any exceptions to the virtual destructor rule?
It is not a must. It's a good habit that can prevent bugs.
If you decide to create a base class that doesn't have a virtual destructor, it is the responsibility of you, the developer, to always ensure that derived objects are deleted as the correct type or as a base type that does have a virtual destructor.
Deleting a derived class object using a pointer to a base class that has a non-virtual destructor results in undefined behavior.
Otherwise you are ok to not to have virtual destructor.
I would like to make an important (at least, in my view) practical amendment to correct answers describing the deletion through base object.
In particular, the destructors are called non-virtually if object life-time is managed through std::shared_ptr<Base> allocated via
std::shared_ptr<Base> sptr = std::make_shared<Derived>(args);
UPD. There is a mark that it is a duplicate of this question. But in that question OP asks HOW to use default to define pure virtual destructor. This question is about what the difference.
In C++ (latest standard if possible) what the real difference between defining pure virtual destructor with empty body implementation and just a empty body (or default)?
Variant 1:
class I1 {
public:
virtual ~I1() {}
};
Variant 2.1:
class I21 {
public:
virtual ~I21() = 0;
};
I21::~I21() {}
Variant 2.2:
class I22 {
public:
virtual ~I22() = 0;
};
I22::~I22() = default;
Update I found at least 1 difference between Variant 1 and Variants 2.1/2.2:
std::is_abstract::value is false for Variant 1, and true for Variants 2.1 and 2.2.
Demo
May be someone can found difference between 2.1 and 2.2?
The difference between I1 and I2*, as you pointed out, is that adding = 0 makes the class abstract. In fact, making the destructor pure virtual is a trick to make a class abstract when you don't have any other function to be pure virtual. And I said it's a trick because the destructor cannot be left undefined if you ever want to destruct any derived class of it (and here you will), then you still need to define the destructor, either empty or defaulted.
Now the difference between empty or defaulted destructor/constructor (I21 and I22) is way more obscure, there isn't much written out there. The recommended one is to use default, both as a new idiom to make your intentions clearer, and apparently, to give the compiler a chance for optimization. Quoting msdn
Because of the performance benefits of trivial special member functions, we recommend that you prefer automatically generated special member functions over empty function bodies when you want the default behavior.
There are no visible differences between the two, apart from this possible performance improvement. = default is the way to go from C++11 on.
All I could find was:
§12.4 (5.9)
A destructor can be declared virtual (10.3) or pure virtual (10.4); if any objects of that class or any
derived class are created in the program, the destructor shall be defined. If a class has a base class with a
virtual destructor, its destructor (whether user- or implicitly-declared) is virtual.
leading to:
§10.4 (the class is now abstract)
10.4 (2) says:
A pure virtual function need be defined only if called with, or as if with (12.4), the qualified-id syntax (5.1).
But the narrative on destructors in §12.4 talks about destructors always being called as if by their fully qualified name (in order to prevent ambiguity).
Which means that:
the destructor must be defined, even if pure virtual, and
the class is now abstract.
Variant 1 will allow you to have an instance of the class. Variant 2.1, 2.2 won't allow instances, but allows instances of descendants. This, for example works (and is able to confuse many people), while removing the marked line will make compile fail:
class I21 {
public:
virtual ~I21() = 0;
};
I21::~I21() {} // remove this and it'll not compile
class I22 : public I21
{
public:
virtual ~I22() {}
};
int main() {
I22 i;
return 0;
}
The reason behind, the destructor chain calls I21::~I21() directly and not via interface. That said, it's not clear what your goal is with pure virtual destructors. If you'd like to avoid instantiation (i.e., static class), you might consider deleting the constructor instead; if you'd like descendants that can be instantiated but not this class, perhaps you need a pure virtual member function that's implemented in descendants.
Consider this code:
class A {
public:
void fun() {}
};
class B : public A {
public:
void fun() {}
};
int main()
{
A *p = new B;
delete p;
}
Classes A and B are not polymorphic, and neither class declares a destructor. If I compile this code with g++ -Wall, the GCC compiler happily compiles the code.
But if I add virtual to void fun() in A, the compiler issues this warning: "deleting object of polymorphic class type ‘A’ which has non-virtual destructor might cause undefined behavior".
I'm quite aware of the dangers of using non-virtual destructors. But the code above makes me wonder about two things:
Why do I need to write an empty virtual destructor in the base class when I'm not using destructors at all?
Why is the empty virtual destructor not required if the base class contains no other virtual functions?
EDIT
It appears that I need to clarify the thing that bothers me:
The above code declares no destructors.
If I declare a virtual function, the compiler complains about the missing virtual destructor. My conclusion: If the class is polymorphic, I need to write a virtual destructor if delete p is to work correctly.
But if I declare no virtual function (as in the initial example above), the compiler does not complain about a missing virtual destructor. My conclusion: If the class is not polymorphic, I do not need to write a virtual desctructor, and delete p will work correctly anyway.
But that last conclusion sounds intuitively wrong to me. Is it wrong? Should the compiler have complained in both cases?
Following up on PaulMcKenzie's and KerrekSB's comments, here is the answer to the two questions in the original post:
The class always has a destructor, even when the programmer doesn't explicitly write one. It is necessary to declare an empty virtual destructor in order to prevent the system from automatically generating a non-virtual one.
In the sample code provided, you do need a virtual destructor, even when there is no other virtual function in the class. The fact that GCC doesn't complain in this case is probably a bug in the compiler (or at least a shortcoming).
The background for this is found in §5.3.5 of the C++11 standard, which says, "if the static type of the object to be deleted is different from its dynamic type, the static type shall be a base class of the dynamic type of the object to be deleted and the static type shall have a virtual destructor or the behavior is undefined." (Italics mine.)
You are making an upcasting, in other words: a polymorphic use of the class B. If the class A has not virtual members, the compiler does not generate VTABLE for class A and it is not require a virtual destructor (note that your upcasting has no sense without polymorphism use). While if the class A declares virtual members, a VTABLE is generated by compiler, in this case you should provide a virtual destructor.
If your want polymorphic behaviour you need to define at least one virtual function in order compiler should generate v-table for your class.
Because C++ class contains two special functions (constructor and destructor) which used for every object it a good choice to make destructor virtual.
When you write delete p you really call a destructor for object associated with pointer p. If you not declare a destructor as virtual you've get error prone code.
Before your declare at least one of member function as virtual compiler did not expect that you intend using your class as polymorphic. In C++ phylosophy: "You should not pay for functionallity that you will never use". E.g. in simple case destructor should not be virtual.
I read that that virtual destructors must be declared in classes that have virtual methods. I just cant understand why they must be declared virtual. I know why we need to have virtual destructors as from the following example. I just wanted to know why compilers dont manage virtual destructors for us. Is there something I need to know about working of virtual destructors ?
The following example shows that if destructors are not declared virtual the destructors of derived class are not called why is that ?
class Base
{
// some virtual methods
public:
Base()
{std::cout << "Base Constructor\n";}
~Base()
{std::cout << "Base De-structor\n";}
};
class Derived : public Base
{
public:
Derived()
{std::cout << "Der constructor\n";}
~Derived()
{ std::cout << "Der De-structor\n";}
} ;
void main()
{
Base *b = new Derived();
delete b;
}
I just wanted to know why compilers dont manage virtual destructors for us.
Because in C++, you pay for what you use. Having a virtual destructor by default involves the compiler adding a virtual table pointer to the class, which increases its size. This is not always desirable.
The following example shows that if destructors are not declared virtual the destructors of derived class are not called why is that ?
The example exibits undefined behavior. It's simply against the rules. The fact that not all destructors are called is just one possible manifestation. It could possibly crash.
Is there something I need to know about working of virtual destructors ?
Yes. They are required if you're deleting an object through a pointer to a base class. Otherwise it's undefined behavior.
5.3.5 Delete [expr.delete]
3) In the first alternative (delete object), if the static type of the object to be deleted is different from its
dynamic type, the static type shall be a base class of the dynamic type of the object to be deleted and the
static type shall have a virtual destructor or the behavior is undefined. In the second alternative (delete
array) if the dynamic type of the object to be deleted differs from its static type, the behavior is undefined. (emphasis mine)
I read that that virtual destructors must be declared in classes that have virtual methods.
"must" is too strong a word: "should" fits much better into that advise.
I just wanted to know why compilers dont manage virtual destructors for us.
C++ designers tried to avoid compiler doing things that you did not ask it to do only under the most extreme circumstances. Language designers recognized that the decision to make a class polymorphic should rest with the designer of the program, so they refused to re-assign this responsibility to the compiler.
The following example shows that if destructors are not declared virtual the destructors of derived class are not called why is that?
Because your code is invalid: by declaring the destructor of Derived non-virtual you made a promise to never destroy Derived through a pointer to Base; your main breaks this promise, invoking undefined behavior.
Note that by merely declaring your b variable with the exact type you would have avoided the problem associated with the non-virtual destructor (link to ideone). However, this leads to a rather shaky design, so you should avoid inheritance hierarchies with virtual functions and non-virtual destructors.
I read that that virtual destructors must be declared in classes that have virtual methods.
Yes. But that is an oversimplification.
Its not that a class with virtual methods needs a virtual destructor. But the way a class with virtual methods is used means that it will usually need a virtual destructor. A virtual destructor is ONLY needed if you delete an object via a pointer to its base class. The problem is that when an object has virtually methods you are usually working with a pointer to its base class even though the actual object is slightly different.
I just cant understand why they must be declared virtual.
It's not that they must. As explained above. This is a result of the usual usage patterns.
I just wanted to know why compilers dont manage virtual destructors for us.
Because it is not always needed. And the ethos of C++ is you don't have to pay for something you don't need it. If the compiler always added virtual destructors to a class with virtual methods then I would have to pay the price of using a virtual destructor even in situations I can prove in my code base that I don't need it.
Is there something I need to know about working of virtual destructors ?
Just that there is a slight cost to using them.
if destructors are not declared virtual the destructors of derived class are not called why is that ?
That is why we have virtual destructors to cause this behavior. If you need this behavior you need to add virtual destructors. But there are cases were virtual destructors may not be required which allows the user of this method not to pay the price.