Why when using std::shared_ptr deallocation calls destructors from both base and derived classes when second example calls only destructor from base class?
class Base
{
public:
~Base()
{
std::cout << "Base destructor" << std::endl;
}
};
class Derived : public Base
{
public:
~Derived()
{
std::cout << "Derived destructor" << std::endl;
}
};
void virtual_destructor()
{
{
std::cout << "--------------------" << std::endl;
std::shared_ptr<Base> sharedA(new Derived);
}
std::cout << "--------------------" << std::endl;
Base * a = new Derived;
delete a;
}
Output:
--------------------
Derived destructor
Base destructor
--------------------
Base destructor
I was expecting the same behaviour in both cases.
delete a
is undefined behaviour, because the class Base
does not have a virtual destructor and the "complete object" of *a
(more accurately: the most-derived object containing *a
) is not of type Base
.
The shared pointer is created with a deduced deleter that deletes a Derived *
, and thus everything is fine.
(The effect of the deduced deleter is to say delete static_cast<Derived*>(__the_pointer)
).
If you wanted to reproduce the undefined behaviour with the shared pointer, you'd have to convert the pointer immediately:
// THIS IS AN ERROR
std::shared_ptr<Base> shared(static_cast<Base*>(new Derived));
In some sense, it is The Right Way for the shared pointer to behave: Since you are already paying the price of the virtual lookup for the type-erased deleter and allocator, it is only fair that you don't then also have to pay for another virtual lookup of the destructor. The type-erased deleter remembers the complete type and thus incurs no further overhead.