I know that I need to define a virtual destructor (best option even if my class is final
).
In my case, I am using C-like structures (no functions, no defaults, just plain members) and use inheritance to compose a new structure. I then store a pointer to the base class in std::unique_ptr
and let RAII do the rest.
I am now curious if there is a need to also explicitely add a virtual destructor to avoid memory problems.
An example might be:
#include <chrono>
#include <memory>
struct A {
std::chrono::milliseconds duration = std::chrono::milliseconds{-1};
int count = 0;
};
struct B {
int mode = 0;
};
struct C : public A, public B {
int foo = 1;
};
int main()
{
std::unique_ptr<A> base = std::make_unique<C>();
base.reset(); // I expect here that A,B and C are destructed properly
return 0;
}
CodePudding user response:
It doesn't matter whether the class is polymorphic or whether it is trivial.
If delete
is called on a pointer of different type (up to cv-qualification) than the most-derived type of the object it points to and the pointed-to-type doesn't have a virtual destructor, then the behavior is undefined.
One obvious reason for this rule is that the base class subobject might not be located at the same address as the most-derived object. So the compiler would have no way of knowing what the offset to pass to the deallocation function needs to be.
One could maybe argue that a standard-layout class with trivial destructor would not need to follow this rule if a pointer to the first base class subobject is used, but the standard doesn't make that exception and your class C
isn't standard-layout anyway.
See CWG issue 1259 closed as not-a-defect. The size-aware global deallocation functions mentioned in the issue were also introduced with C 14, which is another reason that using a base class pointer may cause you problems in practice, even if the destructor is trivial and the address without offset.
CodePudding user response:
If you use shared_ptr
rather than unique_ptr
then this will work as the deleter of the shared_ptr
is created when the first pointer is created and doesn't change when the pointer is copied between shared_ptr
instances. This doesn't apply in unique_ptr
which will use std::default_delete<T>
where T
is the type of the unique_ptr
instance not the type originally constructed.
See https://godbolt.org/z/TjP6dbo9G for some examples of unique_ptr
failing and shared_ptr
working.