What is the cost of using virtual destructors if I use it even if it is not needed?
The cost of introducing any virtual function to a class (inherited or part of the class definition) is a possibly very steep (or not depending on the object) initial cost of a virtual pointer stored per object, like so:
struct Integer
{
virtual ~Integer() {}
int value;
};
In this case, the memory cost is relatively enormous. The actual memory size of a class instance will now often look like this on 64-bit architectures:
struct Integer
{
// 8 byte vptr overhead
int value; // 4 bytes
// typically 4 more bytes of padding for alignment of vptr
};
The total is 16 bytes for this Integer
class as opposed to a mere 4 bytes. If we stored a million of these in an array, we end up with 16 megabytes of memory usage: twice the size of the typical 8 MB L3 CPU cache, and iterating through such an array repeatedly can be many times slower than the 4 megabyte equivalent without the virtual pointer as a result of additional cache misses and page faults.
This virtual pointer cost per object, however, doesn't increase with more virtual functions. You can have 100 virtual member functions in a class and the overhead per instance would still be a single virtual pointer.
The virtual pointer is typically the more immediate concern from an overhead standpoint. However, in addition to a virtual pointer per instance is a per-class cost. Each class with virtual functions generates a vtable
in memory which stores addresses to the functions it should actually call (virtual/dynamic dispatch) when a virtual function call is made. The vptr
stored per instance then points to this class-specific vtable
. This overhead is usually a lesser concern, but it might inflate your binary size and add a bit of runtime cost if this overhead was paid needlessly for a thousand classes in a complex codebase, e.g. This vtable
side of the cost does actually increase proportionally with more and more virtual functions in the mix.
Java developers working in performance-critical areas understand this kind of overhead very well (though often described in the context of boxing), since a Java user-defined type implicitly inherits from a central object
base class and all functions in Java are implicitly virtual (overridable) in nature unless marked otherwise. As a result, a Java Integer
likewise tends to require 16 bytes of memory on 64-bit platforms as a result of this vptr
-style metadata associated per instance, and it's typically impossible in Java to wrap something like a single int
into a class without paying a runtime performance cost for it.
Then the question is: Why doesn't c++ set all destructors virtual by default?
C++ really favors performance with a "pay as you go" kind of mindset and also still a lot of bare-metal hardware-driven designs inherited from C. It doesn't want to needlessly include the overhead required for vtable generation and dynamic dispatch for every single class/instance involved. If performance isn't one of the key reasons you are using a language like C++, you might benefit more from other programming languages out there as a lot of the C++ language is less safe and more difficult than it ideally could be with performance often being the key reason to favor such a design.
When do I NOT need to use virtual destructors?
Quite often. If a class is not designed to be inherited, then it doesn't need a virtual destructor and would only end up paying a possibly large overhead for something it doesn't need. Likewise, even if a class is designed to be inherited but you never delete subtype instances through a base pointer, then it also does not require a virtual destructor. In that case, a safe practice is to define a protected nonvirtual destructor, like so:
class BaseClass
{
protected:
// Disallow deleting/destroying subclass objects through `BaseClass*`.
~BaseClass() {}
};
In which case I should NOT use virtual destructors?
It's actually easier to cover when you should use virtual destructors. Quite often far more classes in your codebase will not be designed for inheritance.
std::vector
, for example, is not designed to be inherited and typically should not be inherited (very shaky design), as that will then be prone to this base pointer deletion issue (std::vector
deliberately avoids a virtual destructor) in addition to clumsy object slicing issues if your derived class adds any new state.
In general a class that's inherited should either have a public virtual destructor or a protected, nonvirtual one. From C++ Coding Standards
, chapter 50:
50. Make base class destructors public and virtual, or protected and nonvirtual. To delete, or not to delete; that is the question: If deletion through a pointer to a base Base should be allowed, then Base's destructor must be public and virtual. Otherwise, it should be protected and nonvirtual.
One of the things C++ tends to kind of implicitly emphasize (because designs tend to get really brittle and awkward and possibly even unsafe otherwise) is the idea that inheritance is not a mechanism designed to be used as an afterthought. It's an extensibility mechanism with polymorphism in mind, but one which requires foresight as to where extensibility is needed. As a result, your base classes should be designed as roots of an inheritance hierarchy upfront, and not something you inherit from later as an afterthought without any such foresight in advance.
In those cases where you simply want to inherit to reuse existing code, composition is often strongly encouraged (Composite Reuse Principle).