When to use virtual destructors?

808,219

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.

Share:
808,219
Lodle
Author by

Lodle

Im a c++ nut. :P

Updated on July 08, 2022

Comments

  • Lodle
    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
      Kiran Kumar over 15 years
      See this: Virtual Destructor
    • Mooing Duck
      Mooing Duck almost 11 years
      Every destructor down gets called no matter what. virtual makes sure it starts at the top instead of the middle.
    • Eitan T
      Eitan T almost 11 years
    • Euri Pinhollow
      Euri Pinhollow almost 7 years
      @MooingDuck that's somewhat misleading comment.
    • Franklin Yu
      Franklin Yu over 6 years
      @EuriPinhollow Mind elaborating?
    • Euri Pinhollow
      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
      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
      Nibor about 5 years
      I'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
      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
      Tunvir Rahman Tusher over 4 years
      this article might help. medium.com/@tunvirrahmantusher/…
  • Lodle
    Lodle over 15 years
    This would explain why i had massive leaks using a factory i made before. All makes sense now. Thanks
  • Giorgio
    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
    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
    bobobobo almost 12 years
    Well, this is a bad example as there are no data members. What if Base and Derived 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
    bobobobo almost 12 years
  • Dragan Ostojic
    Dragan Ostojic over 11 years
    It'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
    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
    cmeub about 11 years
    @Murkantilism, "virtual constructors cannot be done" is true indeed. A constructor cannot be marked virtual.
  • DavidRR
    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 automatically virtual in a derived class, even if it is not declared so.
  • cape1232
    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
    Steven Kramer over 10 years
    @workmad3 non-virtual is not necessary for that, is it? Useless maybe, but not required to be absent.
  • ransh
    ransh over 9 years
    when there are only simple data types (no pointers for example), do we still expect to have memory leak if not using virtual destructors ?
  • Geekoder
    Geekoder over 9 years
    @ransh: as @bobobobo already said in a previous comment, even if the classes have no member variables, calling delete on a Base* that points to a Derived object is undefined behavior. This means that the program can do anything, including leaking memory.
  • dascandy
    dascandy over 8 years
    Classes 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
    Sundae over 8 years
    From the Herb Sutter's article: "Guideline #4: A base class destructor should be either public and virtual, or protected and nonvirtual."
  • ubuntugod
    ubuntugod over 8 years
    I 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
    BigSandwich over 8 years
    Thats pretty much the same thing. The default constructor is not virtual.
  • Bondolin
    Bondolin over 8 years
    Also 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
    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
    underscore_d about 8 years
    This 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
    underscore_d about 8 years
    No, 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 for NULL - which should be nullptr - before deleteing - 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 the new somehow failed.)
  • underscore_d
    underscore_d about 8 years
    I 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
    Ponkadoodle almost 8 years
    Is 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
    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 using override.
  • James Adkison
    James Adkison over 7 years
    It is safe to call delete on a NULL pointer (i.e., you don't need the if (a != NULL) guard).
  • James Adkison
    James Adkison over 7 years
    Not have the base virtual destructor and calling delete on a base pointer leads to undefined behavior.
  • vsoftco
    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 the Derived 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
    rooni over 6 years
    @TunvirRahmanTusher could you please explain why the Base Destructor is called??
  • rooni
    rooni over 6 years
    @JamesAdkison why does it lead to undefined behaviour??
  • Tunvir Rahman Tusher
    Tunvir Rahman Tusher over 6 years
    @rimiro Its automatic by c++.you can follow the link stackoverflow.com/questions/677620/…
  • James Adkison
    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
    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
    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
    Ciro Santilli OurBigBook.com over 6 years
    Why 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 rather Derived1 *b = new Derived1. Also, without new, there can be no problems, as the compiler always knows the real type of whatever goes out of scope.
  • James Adkison
    James Adkison about 6 years
    @SaileshD Yes, I know. That is what I said in my comment
  • ajaysinghnegi
    ajaysinghnegi almost 5 years
    @Sundae Kindly explain the protected and non-virtual part.
  • Sundae
    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
    RetroSeven over 4 years
    Though 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
    fuzzyTew over 4 years
    To 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 a final class never needs a virtual destructor, even if it derives from a class that has one.
  • Geekoder
    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
    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
    Gr-Disarray almost 3 years
    Out 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
    John almost 3 years
    @BigSandwich "hang myself"? You mean memory leaking?
  • John
    John almost 3 years
    @Abyx Is it sutiable to call base->~Base()? As per what you said, the Base::~Base() would not be invoked, then there would be memory leaking. Am I right?
  • Dmitry Ivanov
    Dmitry Ivanov over 2 years
    Strange, 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
    xyf over 2 years
    the first example that has no virtual destructor in the base UB no?
  • user904963
    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
    user904963 over 2 years
    I'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
    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
    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.