for better understand this problem, let's see the code1:
// code1
struct A {
int e;
};
struct B : A {
virtual ~B() { }
};
struct C : B {
~C() { }
};
as you can see, class A is trivially destructible
Q1: is the behavor is undefined if we destory the instance of the class C through the pointer of the class A in code1?
Q2: is the behavor is undefined if we destory the instance of the class C through the pointer of the class B in code1?
here is the another version, call it code2:
//code2
struct A {
int e;
~A() { }
};
struct B : A {
virtual ~B() { }
};
struct C : B {
~C() { }
};
for now, class A has a user-provided destructor. so the class A is not trivially destructible
Q3: is the behavor is undefined if we destory the instance of the class C through the pointer of the class A in code2?
Q4: is the behavor is undefined if we destory the instance of the class C through the pointer of the class B in code2?
I have read the standard ISO/IEC 14882:2011(E) [expr.delete]/3
In the first alternative (delete object), if the static type of the object to be deleted is different from its dynamic type, the static type shall be a base class of the dynamic type of the object to be deleted and the static type shall have a virtual destructor or the behavior is undefined. In the second alternative (delete array) if the dynamic type of the object to be deleted differs from its static type, the behavior is undefined.
Q5: the standard said the static type shall have a virtual destructor, but what about the base class of that static type?
CodePudding user response:
In your example:
A* a = new C();
delete a; // UB
the static type is A
. It doesn't matter that B
makes its destructor virtual. The only destructor that is considered here is A::~A()
(in both your code variations). Now, if you have this:
B* b = new C();
delete b; // not UB
the static type of b
is not A
but B
. B
however has a virtual destructor and will therefore destroy C
first, then B
and finally its base A
. That is no different from a class hierarchy without virtual destructors when you destroy the most derived type:
struct X {};
struct Y : X {};
struct Z : Y {};
Z* z = new Z();
delete z; // destroys Z, then Y, then X (not UB)
for this to work there is little difference with trivial and non-trivial destructors.
To answer your final question: The static type you want to destroy must have a virtual destructor or be identical to the dynamic type. Otherwise everything above the static type will be erroneously overlooked. But types below the static type are statically known to be base classes of the static type, so a virtual destructor is not necessary.