When to use virtual destructors?
Solution 1
Virtual destructors are useful when you might potentially delete an instance of a derived class through a pointer to base class:
class Base
{
// some virtual methods
};
class Derived : public Base
{
~Derived()
{
// Do some important cleanup
}
};
Here, you'll notice that I didn't declare Base's destructor to be virtual
. Now, let's have a look at the following snippet:
Base *b = new Derived();
// use b
delete b; // Here's the problem!
Since Base's destructor is not virtual
and b
is a Base*
pointing to a Derived
object, delete b
has undefined behaviour:
[In
delete b
], 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 most implementations, the call to the destructor will be resolved like any non-virtual code, meaning that the destructor of the base class will be called but not the one of the derived class, resulting in a resources leak.
To sum up, always make base classes' destructors virtual
when they're meant to be manipulated polymorphically.
If you want to prevent the deletion of an instance through a base class pointer, you can make the base class destructor protected and nonvirtual; by doing so, the compiler won't let you call delete
on a base class pointer.
You can learn more about virtuality and virtual base class destructor in this article from Herb Sutter.
Solution 2
A virtual constructor is not possible but virtual destructor is possible. Let us experiment.......
#include <iostream>
using namespace std;
class Base
{
public:
Base(){
cout << "Base Constructor Called\n";
}
~Base(){
cout << "Base Destructor called\n";
}
};
class Derived1: public Base
{
public:
Derived1(){
cout << "Derived constructor called\n";
}
~Derived1(){
cout << "Derived destructor called\n";
}
};
int main()
{
Base *b = new Derived1();
delete b;
}
The above code output the following:
Base Constructor Called
Derived constructor called
Base Destructor called
The construction of derived object follow the construction rule but when we delete the "b" pointer(base pointer) we have found that only the base destructor is called. But this must not happen. To do the appropriate thing, we have to make the base destructor virtual. Now let see what happens in the following:
#include <iostream>
using namespace std;
class Base
{
public:
Base(){
cout << "Base Constructor Called\n";
}
virtual ~Base(){
cout << "Base Destructor called\n";
}
};
class Derived1: public Base
{
public:
Derived1(){
cout << "Derived constructor called\n";
}
~Derived1(){
cout << "Derived destructor called\n";
}
};
int main()
{
Base *b = new Derived1();
delete b;
}
The output changed as following:
Base Constructor Called
Derived Constructor called
Derived destructor called
Base destructor called
So the destruction of the base pointer (which takes an allocation on derived object!) follows the destruction rule, i.e first the Derived, then the Base. On the other hand, there is nothing like a virtual constructor.
Solution 3
Declare destructors virtual in polymorphic base classes. This is Item 7 in Scott Meyers' Effective C++. Meyers goes on to summarize that if a class has any virtual function, it should have a virtual destructor, and that classes not designed to be base classes or not designed to be used polymorphically should not declare virtual destructors.
Solution 4
Also be aware that deleting a base class pointer when there is no virtual destructor will result in undefined behavior. Something that I learned just recently:
How should overriding delete in C++ behave?
I've been using C++ for years and I still manage to hang myself.
Solution 5
Make the destructor virtual whenever your class is polymorphic.
Comments
-
Lodle almost 2 years
I have a solid understanding of most
OOP
theory but the one thing that confuses me a lot is virtual destructors.I thought that the destructor always gets called no matter what and for every object in the chain.
When are you meant to make them virtual and why?
-
Kiran Kumar over 15 yearsSee this: Virtual Destructor
-
Mooing Duck almost 11 yearsEvery destructor down gets called no matter what.
virtual
makes sure it starts at the top instead of the middle. -
Eitan T almost 11 yearsrelated question: When should you not use virtual destructors?
-
Euri Pinhollow almost 7 years@MooingDuck that's somewhat misleading comment.
-
Franklin Yu over 6 years@EuriPinhollow Mind elaborating?
-
Euri Pinhollow over 6 years@FranklinYu it's good that you asked because now I can't see any issue with that comment (except trying to give answer in comments).
-
Euri Pinhollow over 6 years@FranklinYu I probably thought about that there should be more specific claim (i.e. when destruction can start in the middle of inheritance tree instead of top) but that's what answers already elaborate.
-
Nibor about 5 yearsI'm also confused by @MooingDuck 's answer. Shouldn't it be up instead of down, if you use the notion of subclass (under) and superclass (above)?
-
Mooing Duck about 5 years@Nibor: Yes, if you use that notion. About half the people I talk to view superclasses as "above", and half view superclasses as "below", so both are conflicting standards, which makes everything confusing. I think superclass as "above" is slightly more common, but that's not the way I was taught :(
-
Tunvir Rahman Tusher over 4 yearsthis article might help. medium.com/@tunvirrahmantusher/…
-
-
Lodle over 15 yearsThis would explain why i had massive leaks using a factory i made before. All makes sense now. Thanks
-
Giorgio about 12 years+"If a class has any virtual function, it should have a virtual destructor, and that classes not designed to be base classes or not designed to be used polymorphically should not declare virtual destructors.": Are there cases in which it makes sense to break this rule? If not, would it make sense to have the compiler check this condition and issue an error is it is not satisfied?
-
Bill the Lizard about 12 years@Giorgio I don't know of any exceptions to the rule. But I wouldn't rate myself as a C++ expert, so you may want to post this as a separate question. A compiler warning (or a warning from a static analysis tool) makes sense to me.
-
bobobobo almost 12 yearsWell, this is a bad example as there are no data members. What if
Base
andDerived
have all automatic storage variables? ie there is no "special" or additional custom code to execute in the destructor. Is it ok then to leave off writing any destructors at all? Or will the derived class still have a memory leak? -
bobobobo almost 12 years
-
Dragan Ostojic over 11 yearsIt's a different perspective on the same question. If we think in terms of interfaces instead of base class vs derived class then it's natural conclusion: if it's a part of the interface than make it virtual. If it's not don't.
-
Tunvir Rahman Tusher about 11 years" virtual constructor is not possible" means you need not write virtual constructor by your own. Construction of derived object must follow the chain of construction from derived to base. So you need not write the virtual keyword for your constructor. Thanks
-
cmeub about 11 years@Murkantilism, "virtual constructors cannot be done" is true indeed. A constructor cannot be marked virtual.
-
DavidRR almost 11 years+1 for stating the similarity of the OO concept of interface and a C++ pure virtual class. Regarding destructor is expected to be implemented: that is often unnecessary. Unless a class is managing a resource such as raw dynamically allocated memory (e.g., not via a smart pointer), a file handle or a database handle, using the default destructor created by the compiler is fine in derived classes. And note that if a destructor (or any function) is declared
virtual
in a base class, it is automaticallyvirtual
in a derived class, even if it is not declared so. -
cape1232 over 10 years@cmeub, But there is an idiom to achieve what you would want from a virtual constructor. See parashift.com/c++-faq-lite/virtual-ctors.html
-
Steven Kramer over 10 years@workmad3 non-virtual is not necessary for that, is it? Useless maybe, but not required to be absent.
-
ransh over 9 yearswhen there are only simple data types (no pointers for example), do we still expect to have memory leak if not using virtual destructors ?
-
Geekoder over 9 years@ransh: as @bobobobo already said in a previous comment, even if the classes have no member variables, calling
delete
on aBase*
that points to aDerived
object is undefined behavior. This means that the program can do anything, including leaking memory. -
dascandy over 8 yearsClasses can be designed not to be deleted through a certain type's pointer, yet still have virtual functions - typical example is a callback interface. One does not delete his implementation through a callback interface pointer as that's only for subscribing, but it does have virtual functions.
-
Sundae over 8 yearsFrom the Herb Sutter's article: "Guideline #4: A base class destructor should be either public and virtual, or protected and nonvirtual."
-
ubuntugod over 8 yearsI had a look at that question of yours and saw that you had declared the base destructor as virtual. So does "deleting a base class pointer when there is no virtual destructor will result in undefined behavior" stay valid with respect to that question of yours? Since, in that question, when you called delete, the derived class (created by its new operator) is checked for a compatible version first. Since it found one there, it was called. So, don't you think it would be better to say as "deleting a base class pointer when there is no destructor will result in undefined behavior"?
-
BigSandwich over 8 yearsThats pretty much the same thing. The default constructor is not virtual.
-
Bondolin over 8 yearsAlso from the article - 'if you delete polymorphically without a virtual destructor, you summon the dreaded specter of "undefined behavior," a specter I personally would rather not meet in even a moderately well-lit alley, thank you very much.' lol
-
underscore_d about 8 years@dascandy Exactly - that or all the many other situations where we use polymorphic behaviour but don't perform storage management via pointers - e.g. maintaining automatic or static-duration objects, with pointers only used as observation routes. No need/purpose in implementing a virtual destructor in any such cases. Since we're just quoting people here, I prefer Sutter from above: "Guideline #4: A base class destructor should be either public and virtual, or protected and nonvirtual." The latter ensures anyone accidentally trying to delete via a base pointer is shown the error of their ways
-
underscore_d about 8 yearsThis misses the crucial detail that the destructor is not necessarily part of the interface. One can easily program classes that have polymorphic functions but which the caller does not manage/is not allowed to delete. Then a virtual destructor has no purpose. Of course, to ensure this, the non-virtual - probably default - destructor should be non-public. If I had to guess, I'd say such classes are more often used internally to projects, but that doesn't make them any less relevant as an example/nuance in all this.
-
underscore_d about 8 yearsNo, this only retreads the utter basics of virtual functions, totally ignoring the nuance of when/why the destructor should be one - which isn't as intuitive, hence why the OP asked the question. (Also, why the unnecessary dynamic allocation here? Just do
B b{}; A& a{b}; a.foo();
. Checking forNULL
- which should benullptr
- beforedelete
ing - with incorrect indendation - is not required:delete nullptr;
is defined as a no-op. If anything, you should have checked this before calling->foo()
, as otherwise undefined behaviour can occur if thenew
somehow failed.) -
underscore_d about 8 yearsI would say this is only necessary "if it can be pointed to by a base class pointer" and can be publically deleted. But I guess it doesn't hurt to get into the habit of adding virtual dtors in case they might become needed later.
-
Ponkadoodle almost 8 yearsIs it necessary to explicitly declare
~Derived()
in all derived classes, even if it's just~Derived() = default
? Or is that implied by the language (making it safe to omit)? -
Abyx almost 8 years@Wallacoloo no, only declare it when it's necessary. E.g. to put in
protected
section, or to ensure that it's virtual by usingoverride
. -
James Adkison over 7 yearsIt is safe to call
delete
on aNULL
pointer (i.e., you don't need theif (a != NULL)
guard). -
James Adkison over 7 yearsNot have the base virtual destructor and calling
delete
on a base pointer leads to undefined behavior. -
vsoftco over 7 years@Giorgio There is actually a trick one can use and avoid a virtual call to a destructor: bind via a const reference a derived object to a base, like
const Base& = make_Derived();
. In this case, the destructor of theDerived
prvalue will be called, even if it's not virtual, so one saves the overhead introduced by vtables/vpointers. Of course the scope is quite limited. Andrei Alexandrescu mentioned this in his book Modern C++ Design. -
rooni over 6 years@TunvirRahmanTusher could you please explain why the Base Destructor is called??
-
rooni over 6 years@JamesAdkison why does it lead to undefined behaviour??
-
Tunvir Rahman Tusher over 6 years@rimiro Its automatic by c++.you can follow the link stackoverflow.com/questions/677620/…
-
James Adkison over 6 years@rimiro It's what the standard says. I don't have a copy but the link takes you to a comment where someone references the location within the standard.
-
James Adkison over 6 years@rimiro "If deletion, therefore, can be performed polymorphically through the base class interface, then it must behave virtually and must be virtual. Indeed, the language requires it - if you delete polymorphically without a virtual destructor, you summon the dreaded specter of "undefined behavior," a specter I personally would rather not meet in even a moderately well-lit alley, thank you very much." (gotw.ca/publications/mill18.htm) -- Herb Sutter
-
davidvandebunte over 6 years@Giorgio there is a warning in gcc for just this situation. dascandy they initially failed to consider your point as documented here.
-
Ciro Santilli OurBigBook.com over 6 yearsWhy do things work if there is no virtual method without a polymorphic destructor? EDIT answer: they don't, but in that case, you would never need to write
Base *b = new Derived1
, which is the source of all problems, but ratherDerived1 *b = new Derived1
. Also, withoutnew
, there can be no problems, as the compiler always knows the real type of whatever goes out of scope. -
James Adkison about 6 years@SaileshD Yes, I know. That is what I said in my comment
-
ajaysinghnegi almost 5 years@Sundae Kindly explain the
protected and non-virtual part
. -
Sundae almost 5 years@Jos: From the article: "In brief, then, you're left with one of two situations. Either: a) you want to allow polymorphic deletion through a base pointer, in which case the destructor must be virtual and public; or b) you don't, in which case the destructor should be nonvirtual and protected, the latter to prevent the unwanted usage."
-
RetroSeven over 4 yearsThough this is possible, I would discourage anyone from using this. The overhead of a virtual destructor is miniscule and this just makes it possible to mess up, specially by a less experienced programmer, who doesn't know this. That little
virtual
keyword could save you from a lot of agony. -
fuzzyTew over 4 yearsTo clarify, it sounds like a virtual destructor is needed if the type will be passed to
delete
when it was allocated as a derived type. The derived type does not need a virtual destructor, unless the same applies to it. So afinal
class never needs a virtual destructor, even if it derives from a class that has one. -
Geekoder over 4 years@fuzzyTew If a class D derives from a class B with a virtual destructor, then D has a virtual destructor as well, even if it doesn't declare one explicitly. Most of the time only the class at the top of the hierarchy needs to declare a virtual destructor, not the rest of the hierarchy.
-
Steven Lee over 3 years@Luc Touraille: yes, your conclusion solves my confusion. Any non-abstract class can always be instantiated. So to prevent this 'undefined behavior', any non-abstract class should always be defined with virtual destructor. But most of the classes I worked with do not have a virtual destructor at all. So maybe, one of their base classes have one already!
-
Gr-Disarray almost 3 yearsOut of curiosity - Why is it that in the case of a shared_ptr the Base destructor gets called but not in the case of a unique_ptr?
-
John almost 3 years@BigSandwich "hang myself"? You mean memory leaking?
-
John almost 3 years@Abyx Is it sutiable to call
base->~Base()
? As per what you said, theBase::~Base()
would not be invoked, then there would be memory leaking. Am I right? -
Dmitry Ivanov over 2 yearsStrange, why in all discussions about "virtual ~" mentioned only heap objects. For example: If we are planning to use objects only in the stack, the base class can omit virtualization. BderiveA objB;// For class 'A' no need virtual ~.
-
xyf over 2 yearsthe first example that has no virtual destructor in the base UB no?
-
user904963 over 2 years@underscore_d People usually demonstrate the behavior using pointers, because the most common use cases use pointers such as having
std::vector<Base*>
. Of course,std::vector<Base&>
isn't a thing. -
user904963 over 2 yearsI'd recommend not using the massive number of implied behaviors in C++. You can in your own project, but anywhere else, explicit code conveys intent rather than just behavior plus someone else working on the project may not know C++ perfectly. For example, do you know the default linkage behavior of a
const
global variable versus a non-const
one? Even if you do, I guarantee most don't with many not even knowing the two types of linkage exist. -
John about 2 years@LucTouraille If there is no virtual member function in the said code snippet. Do I still need to mark the destructor of the base class as virtual function? Looking forward for your reply.
-
Geekoder about 2 years@John yes you do, the presence or absence of other virtual members doesn't change the requirement: you would still have undefined behavior if you deleted a Derived object from a Base pointer.