If a Base class does not have a virtual destructor (in order to avoid the vtable entry for instance) and the Derived class has only basic attributes, does it free all the memory allocated by new, when the pointer of the Base class is deleted? I know the destructor of the Derived class will not be called but I am wondering if the memory allocated by the whole object will be freed?
I assume also that calling delete on a Derived pointer will free the whole memory space.
Also, if it does not free the Derived class part of the memory, how does it work in the same case but with of a virtual destructor in the Base class, to know how much memory to free?
Example:
class Base {
public:
int a;
int b;
Base() {}
~Base() {}
};
class Derived : public Base {
public:
int c;
int d;
Derived() {}
~Derived() {}
};
int main() {
Base *p = new Derived();
delete p; // is memory allocated for Derived freed?
}
It’s undefined behavior, so anything can happen. Quote from the standard [expr.delete]:
Just don’t do that.
I think it’s a good idea to understand what actually happens though to understand why the standard has that requirement.
In your simple case, on a typical implementation, the memory will be freed regardless. That is because
operator deletewill callfree(p). SinceBaseis the first non-virtual base class ofDerived, it happens to be located at the beginning of the allocated memory block. And sincefreemust know the size of the allocated block by its own bookkeeping (being a C function it doesn’t know anything about sizes of types), it will deallocate the entire block.However, since
Basedoesn’t have a virtual destructor,delete pis resolved based on the static-type of*p(which isBase). Consequently, as you’re seemingly aware, it will not call the destructor ofDerived. IfDerivedhad any non-trivial members or base-classes, those would not be destructed either, so any resources that they manage would be leaked.When the class has a virtual destructor, the work of freeing the memory is delegated to the destructor. The reason is that, although
freeknows to determine the size of the block, it would still need to have the pointer to the beginning of it.Basecan be at an arbitrary offset withinDerivedin the general case, thereforeDeriveddestructor will be responsible for adjusting the pointer prior to freeing it. This also adds the need for a separate destructor that would destructor the object without actually freeing the memory; e.g. as a subobject of another derived type, or as explicit destructor call. And let’s keepdelete[]out of the discussion. When you mark your destructor as virtual, the compiler takes care of all this for you.The bottom line is that the standard doesn’t describe all these implementation details, leaving them instead to—um—the implementation. They could formulate some sensible criteria when deleting a non-virtual base would still be ok; but instead they went with the simple clear-cut "you have a virtual destructor then it’s ok, otherwise it’s not ok" rule.