default override of virtual destructor

C++C++11C++14

C++ Problem Overview


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?

C++ Solutions


Solution 1 - C++

> 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.

Solution 2 - C++

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).

Solution 3 - C++

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.).

Solution 4 - C++

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.

Solution 5 - C++

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

Solution 6 - C++

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.

Solution 7 - C++

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.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionSandroView Question on Stackoverflow
Solution 1 - C++Kuba hasn't forgotten MonicaView Answer on Stackoverflow
Solution 2 - C++SergeyAView Answer on Stackoverflow
Solution 3 - C++Alexandre A.View Answer on Stackoverflow
Solution 4 - C++cyberbissonView Answer on Stackoverflow
Solution 5 - C++hakun bahunView Answer on Stackoverflow
Solution 6 - C++Martin UedingView Answer on Stackoverflow
Solution 7 - C++Vlad from MoscowView Answer on Stackoverflow