Why doesn't C++ make destructors virtual by default for classes that have at least one other virtual function? In this case adding a virtual destructor costs me nothing, and not having one is (almost?) always a bug. Will C++0x address this?
The destructor is not user-provided (meaning, it is either implicitly declared, or explicitly defined as defaulted on its first declaration) The destructor is not virtual (that is, the base class destructor is not virtual)
Virtual destructors in C++ are used to avoid memory leaks especially when your class contains unmanaged code, i.e., contains pointers or object handles to files, databases or other external objects. A destructor can be virtual.
The default destructor calls the destructors of the base class and members of the derived class. The destructors of base classes and members are called in the reverse order of the completion of their constructor: The destructor for a class object is called before destructors for members and bases are called.
Deleting a derived class object using a pointer of base class type that has a non-virtual destructor results in undefined behavior.
You don't pay for what you don't need. If you never delete through base pointer, you may not want the overhead of the indirected destructor call.
Perhaps you were thinking that the mere existence of the vtable is the only overhead. But each individual function dispatch has to be considered, too, and if I want to make my destructor call dispatch directly, I should be allowed to do so.
It would be nice of your compiler to warn you if you do ever delete a base pointer and that class has virtual methods, I suppose.
Edit: Let me pull Simon's excellent comment in here: Check out this SO question on the code generated for destructors. As you can see, there's also code-bloat overhead to be considered.
Here's an example (not that I recommend writing such code):
struct base { virtual void foo() const = 0; virtual void bar () const = 0; }; struct derived: base { void foo() const {} void bar() const {} }; std::shared_ptr<base> make_base() { return std::make_shared<derived>(); }
This is perfectly fine code that does not exhibit UB. This is possible because std::shared_ptr
uses type-erasure; the final call to delete
will delete a derived*
, even if the last std::shared_ptr
to trigger destruction is of type std::shared_ptr<void>
.
Note that this behaviour of std::shared_ptr
is not tailored to virtual destruction; it has a variety of other uses (e.g. std::shared_ptr<FILE> { std::fopen( ... ), std::fclose }
). However since this technique already pays the cost of some indirection to work, some users may not be interested in having a virtual destructor for their base classes. That's what "pay only for what you need" means.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With