Derived class with non-virtual destructor
Asked Answered
J

11

83

Are there any circumstances in which it is legitimate for a derived class to have a non-virtual destructor? A non-virtual destructor signifies that a class should not be used as a base-class. Will having a non-virtual destructor of a derived class act like a weak form of the Java final modifier?

I am especially interested in the case where the base class of the derived class has a virtual destructor.

Jamnis answered 13/9, 2011 at 14:51 Comment(1)
H
113

Are there any circumstances in which it is legitimate for a derived class to have a non-virtual destructor?

Yes.

A non-virtual destructor signifies that a class should not be used as a base-class.

Not really; a non-virtual destructor signifies that deleting an instance of derived via a base pointer will not work. For example:

class Base {};
class Derived : public Base {};

Base* b = new Derived;
delete b; // Does not call Derived's destructor!

If you don't do delete in the above manner, then it will be fine. But if that's the case, then you would probably be using composition and not inheritance.

Will having a non-virtual destructor of a derived class act like a weak form of the Java final modifier?

No, because virtual-ness propagates to derived classes.

class Base
{
public:
    virtual ~Base() {}
    virtual void Foo() {};
};

class Derived : public Base
{
public:
    ~Derived() {}  // Will also be virtual
    void Foo() {}; // Will also be virtual
};

Here is the excerpt from the C++11 standard that formalizes this:

[...] If a class has a base class with a virtual destructor, its destructor (whether user- or implicitly- declared) is virtual.

- [class.dtor] p9

There isn't a built-in language mechanism in C++03 or earlier to prevent subclasses(*). Which isn't much of an issue anyway since you should always prefer composition over inheritance. That is, use inheritance when a "is-a" relationship makes more sense than a true "has-a" relationship.

(*) 'final' modifier was introduced in C++11

Hazem answered 13/9, 2011 at 14:54 Comment(2)
On a related note, if you have a non-virtual Base class, and you have some virtual methods in your Derived class, then Base *b = new Derived(); delete b; will be undefined behaviour and possibly crash your program. It looks quite safe, but it's not. (It's because b won't point at the 'start' of the Derived object - it will be offset by the space needed for the vtable. Then, the delete will not be operating on exactly the same address as the new, and therefore it's not a valid address to free. If you are going to have virtual methods anywhere, then put a virtual d'tor in the base.Lippmann
@AaronMcDaid it will be offset by the space needed for the vtable, it is the vptr not the vtable, vtable is per class and vptr is per object. It is the vptr that is contained in the object.Homicidal
B
38

It is perfectly valid to have an Base class with an non virtual destructor if you are never going to call delete on a Base class pointer pointing to an derived class object.

Follow Herb Sutter's Advice:

Guideline #: Only if derived classes need to invoke the base implementation of a virtual function, make the virtual function protected. For the special case of the destructor only:

Guideline #: A base class destructor should be either public and virtual, or protected and nonvirtual.


Maybe your question actually is:
Does Destructor in Derived class needs to be virtual if Base class Destructor is virtual?

The answer is NO.
If Base class destructor is virtual then the Derived class destructor is implicitly virtual already, you don't need to specify it as virtual explicitly.

Briton answered 13/9, 2011 at 15:5 Comment(2)
"Does Destructor in Derived class needs to be virtual if Base class Destructor is virtual?" Yes, that is really my question.Jamnis
If no data is allocated (stack, heap) in the derived class (i.e. size of base and derived objects are equal), you can still go with the public non-virtual base destructor. This can be a solution for "typedefs" which need to be cast explicitly: a vector base class (of some non-modifiable API), with point, normal and direction subclasses with explicit constructors.Prolonge
S
16

Addresssing the latest edit:

Edit: I am especially interested in the case where the base class of the derived class has a virtual destructor.

In that case, the destructor of the derived class will be virtual, regardless of whether you add the virtual keyword or not:

struct base {
   virtual ~base() {}       // destructor is virtual
};
struct derived : base {
   ~derived() {}            // destructor is also virtual, because it is virtual in base
};

This is not limited to destructors, if at any point in a type hierarchy a function member is declared virtual, all overrides (not overloads) of that same function will be virtual, whether they are declared as so or not. The specific bit for destructors is that ~derived() overrides virtual ~base() even if the name of the member differs --that is the only specificity for destructors here.

Somnambulate answered 13/9, 2011 at 16:26 Comment(0)
C
5

You're question isn't really clear. If the base class has a virtual destructor, the derived class will have one, regardless. There's no way to turn virtuality off, once it's been declared.

And there are certainly cases where it makes sense to derive from a class which doesn't have a virtual destructor. The reason why the base class destructor should be virtual is so that you can delete through a pointer to the base class. If the derivation is private, you don't have to worry about this, since your Derived* won't convert to a Base*. Otherwise, I've seen the recommendation that if the base class destructor isn't virtual, it should be protected; this prevents the one case of undefined behavior (deleting through a pointer to base) that could occur. In practice, however, a lot of base classes (e.g. std::iterator<>) have semantics such that it doesn't even occur to anyone to create pointers to them; much less delete through such pointers. So adding the protection may be more effort than it's worth.

Cheery answered 13/9, 2011 at 15:12 Comment(1)
"this prevents the one case of undefined behavior (deleting through a pointer to base) that could occur" -- You could have a base that is useful on its own and is non-virtual though right?Prankster
Q
4

Depends on the purpose of your class. Sometimes it is a good practice to make your destructor protected, but not virtual - that basically says: "You shall not delete an object of derived class via a base-type pointer"

Quicksand answered 13/9, 2011 at 15:6 Comment(0)
T
2

If your derived class doesn't add any data members to the base class, and has an empty destructor body, then it won't matter if the destructor is virtual or not - all the derived destructor will do is call the base one anyway. It isn't recommended because it's far too easy for someone to come along and modify the class without being aware of these restrictions.

If you never try to delete an object through a pointer to the base class, you'll be safe. This is another rule that's hard to enforce and should be used with care.

Sometimes you don't have any control over the base class and you're forced to derive from it, even though the destructor isn't virtual.

Finally, having a non-virtual destructor in the base class doesn't impose any restriction on the derived class that will be enforced by the compiler, so I don't think it resembles Java's final at all.

Tripp answered 13/9, 2011 at 15:8 Comment(1)
Hear hear. I was obliged to derive from std::vector, which has a non-virtual destructor, a while ago, to maintain backward compatibility. The new class added no data members and had no destructor of its own, so everything was fine; and was guaranteed to be fine. According to my understanding of the C++ standard this behaviour is standard (in this special case) and there can never be undefined behaviour.Ashanti
E
1

A non-virtual destructor is perfectly fine as long as you you don't want to use it as a base pointer for derived classes when deleting the object.

If you its derived classes in a polymorphic way, passing and storing it with a base pointer and then deleting it then the answer is no, use a virtual destructor.

Energetic answered 13/9, 2011 at 14:55 Comment(2)
My comment on : A non-virtual destructor is perfectly fine as long as you don't want to delete the object with a pointer of the type one of its superclasses. ... Even if the destructor is virtual, deleting the object using a pointer of superclass type, will invoke undefined behaviour IF the superclass destructor is not virtual.Poliomyelitis
That is still not correct. You need to edit the old sentence.Poliomyelitis
S
1

Yes, there are:

void dothis(Base const&);

void foo() {
  Derived d;
  tothis(d);
}

Here the class is used polymorphically, yet delete is not called, thus it's fine.

Another example would be:

std::shared_ptr<Base> create() { return std::shared_ptr<Base>(new Derived); }

because a shared_ptr is able to use a non-polymorphic delete (through type erasure).

I implemented a warning in Clang specifically to detect the call of delete on polymorphic non-final classes with non-virtual destructors, so if you use clang -Wdelete-non-virtual-dtor, it will warn specifically for this case.

Sculpin answered 13/9, 2011 at 15:7 Comment(0)
G
0

Yes, No and No.

Virtual destructor has nothing to do with ability of the class to be a base or a derived class. It is a legitimate one as both.

However, there are certain reasons to make destructors virtual. See here: http://en.wikipedia.org/wiki/Virtual_destructor#Virtual_destructors . This makes a class, among other things, have a virtual table if it doesn't have one already. However, virtual tables are not required by C++ to do inheritance.

Grandmother answered 13/9, 2011 at 14:53 Comment(1)
Wikipedia is great for a start point in research it should not be used as a reference source. wikihow.com/Cite-a-Wikipedia-Article-in-MLA-FormatChloe
C
0

Will having a non-virtual destructor of a derived class act like a weak form of the Java final modifier?

Not at all. Here is my suggestion to prevent sub classes in C++ (like final modifier in Java); make the destructor private in a class. Then you can prevent making sub-classes from it

Crandale answered 18/11, 2013 at 3:59 Comment(0)
E
0

You may not want to create virtual destructor in base class? No destructor do in this case. If you use pointer to base class and create non-virtual destructor in parent one compilator automatically generate this warning! You may block it if you want to create final parent class. But best is to mark it as a final like:

class Base{
public:
    //No virtual destructor defined
    virtual void Foo() {};
};

class Derived final : public Base{
public:
    ~Derived() {}  // define some non-virtual destructor
    void Foo() {}; // Will also be virtual
};

In this case compilator knows what you want and no warnings generate. The decision with empty virtual base destructor is not too well but acceptable. You needn't set attribute final. But this isn't the thing you want.You also needn't define empty virtual base method Foo too Best is:

class Base{
public:
  //No virtual destructor defined
  virtual void Foo() = 0; // abstract method
};
class Derived final : public Base{
public:
  ~Derived() {}  // define some non-virtual destructor
  void Foo() {}; // Will also be virtual
};

It's full clear code for compilator. No warnings generate and no spare code used too.

Ezra answered 24/10, 2016 at 4:36 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.