Are there any specific reasons to use non-virtual destructors?
Asked Answered
D

5

21

As I know, any class that is designated to have subclasses should be declared with virtual destructor, so class instances can be destroyed properly when accessing them through pointers.

But why it's even possible to declare such class with non-virtual destructor? I believe compiler can decide when to use virtual destructors. So, is it a C++ design oversight, or am I missing something?

Demurrage answered 2/1, 2012 at 5:59 Comment(4)
+1. I wanted to ask a similar question: if a base class has a virtual function, then why do we still need to make the destructor virtual? Why not the compiler make/consider it virtual on its own?Dragonnade
When the derived (subclass) destructor, doesn't do anything.Emad
See Raymond Chen's blog.Acidity
See also #7404383Vireo
C
20

Are there any specific reasons to use non-virtual destructors?

Yes, there are.

Mainly, it boils down to performance. A virtual function cannot be inlined, instead you must first determined the correct function to invoke (which requires runtime information) and then invoke that function.

In performance sensitive code, the difference between no code and a "simple" function call can make a difference. Unlike many languages C++ does not assume that this difference is trivial.

But why it's even possible to declare such class with non-virtual destructor?

Because it is hard to know (for the compiler) if the class requires a virtual destructor or not.

A virtual destructor is required when:

  • you invoke delete on a pointer
  • to a derived object via a base class

When the compiler sees the class definition:

  • it cannot know that you intend to derive from this class -- you can after all derive from classes without virtual methods
  • but even more daunting: it cannot know that you intend to invoke delete on this class

Many people assume that polymorphism requires newing the instance, which is just sheer lack of imagination:

class Base { public: virtual void foo() const = 0; protected: ~Base() {} };

class Derived: public Base {
  public: virtual void foo() const { std::cout << "Hello, World!\n"; }
};

void print(Base const& b) { b.foo(); }

int main() {
  Derived d;
  print(d);
}

In this case, there is no need to pay for a virtual destructor because there is no polymorphism involved at the destruction time.

In the end, it is a matter of philosophy. Where practical, C++ opts for performance and minimal service by default (the main exception being RTTI).


With regards to warning. There are two warnings that can be leveraged to spot the issue:

  • -Wnon-virtual-dtor (gcc, Clang): warns whenever a class with virtual function does not declare a virtual destructor, unless the destructor in the base class is made protected. It is a pessimistic warning, but at least you do not miss anything.

  • -Wdelete-non-virtual-dtor (Clang, ported to gcc too): warns whenever delete is invoked on a pointer to a class that has virtual functions but no virtual destructor, unless the class is marked final. It has a 0% false positive rate, but warns "late" (and possibly several times).

Coolth answered 2/1, 2012 at 7:39 Comment(1)
@Nawaz: thanks for the notice, it allowed me to edit and note that gcc has acquired my little warning now :)Coolth
J
3

Why are destructors not virtual by default? http://www2.research.att.com/~bs/bs_faq2.html#virtual-dtor

Guideline #4: A base class destructor should be either public and virtual, or protected and nonvirtual. http://www.gotw.ca/publications/mill18.htm

See also: http://www.erata.net/programming/virtual-destructors/

EDIT: possible duplicate? When should you not use virtual destructors?

Jewish answered 2/1, 2012 at 6:56 Comment(0)
M
2

Your question is basically this, "Why doesn't the C++ compiler force your destructor to be virtual if the class has any virtual members?" The logic behind this question is that one should use virtual destructors with classes that they intend to derive from.

There are many reasons why the C++ compiler doesn't try to out-think the programmer.

  1. C++ is designed on the principle of getting what you pay for. If you want something to be virtual, you must ask for it. Explicitly. Every function in a class that is virtual must be explicitly declared so (unless its overriding a base class version).

  2. if the destructor for a class with virtual members were automatically made virtual, how would you choose to make it non-virtual if that's what you so desired? C++ doesn't have the ability to explicitly declare a method non-virtual. So how would you override this compiler-driven behavior.

    Is there a particular valid use case for a virtual class with a non-virtual destructor? I don't know. Maybe there's a degenerate case somewhere. But if you needed it for some reason, you wouldn't be able to say it under your suggestion.

The question you should really ask yourself is why more compilers don't issue warnings when a class with virtual members doesn't have a virtual destructor. That's what warnings are for, after all.

Mallet answered 2/1, 2012 at 6:55 Comment(1)
I kind of agree with the warnings being probably a good idea -- then again, in practice you also have people getting confused and/or complaining about them, as in the case of GCC's "class has virtual functions and accessible non-virtual destructor": #5828219 ; Not sure what's the solution to that -- suggestions and rationale from the compiler? "Please write safe code" or perhaps quoting the Guideline #4 from the GotW I've posted earlier :-)Jewish
P
1

A non-virtual destructor seems to make sense, when a class is just non-virtual after all (Note 1).

However, I do not see any other good use for non-virtual destructors.

And I appreciate that question. Very interesting question!

EDIT:

Note 1: In performance-critical cases, it may be favourable to use classes without any virtual function table and thus without any virtual destructors at all.

For example: think about a class Vector3 that contains just three floating point values. If the application stores an array of them, then that array could be store in compact fashion.

If we require a virtual function table, AND if we'd even require storage on heap (as in Java & co.), then the array would just contain pointers to actual elements "SOMEWHERE" in memory.

EDIT 2:

We may even have an inheritance tree of classes without any virtual methods at all.

Why?

Because, even if having "virtual" methods may seem to be the common and preferable case, it IS NOT the only case that we - the mankind - can imagine.

As in many details of that language, C++ offers you a choice. You can choose one of the provided options, usually you will choose the one that anyone else chooses. But sometimes you do not want that option!

In our example, a class Vector3 could inherit from class Vector2, and still would not have the overhead of virtual functions calls. Thought, that example is not very good ;)

Ph answered 2/1, 2012 at 6:8 Comment(0)
D
1

Another reason I haven't seen mentioned here are DLL boundaries: You want to use the same allocator to free the object that you used to allocate it.

If the methods live in a DLL, but the client code instantiates the object with a direct new, then the client's allocator is used to obtain the memory for the object, but the object is filled in with the vtable from the DLL, which points to a destructor that uses the allocator the DLL is linked against to free the object.

When subclassing classes from the DLL in the client, the problem goes away as the virtual destructor from the DLL is not used.

Dipietro answered 2/1, 2012 at 7:28 Comment(2)
The destructor doesn't deallocate memory. It is called BY the function that deallocates memory. Your answer might be true if the class overloads the new() and delete() operators, but otherwise, I think not.Brodie
If a derived class overrides operator delete, then the code destroying the object via a base pointer does not know, so either you invent a mechanism for the destructor to return whether the memory has already been deallocated, or have the destructor call the deallocation function directly. Both G++ and MSVC do the latter.Dipietro

© 2022 - 2024 — McMap. All rights reserved.