0

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?

iTruth
  • 123
  • 7
  • One question per post. You have 5 questions in your post. – Jason Dec 08 '22 at 05:19
  • @JasonLiam all of these questions are trying to explan one problem clearly, I don't think these questions can be asked separately – iTruth Dec 08 '22 at 05:27
  • You might find it easier to forget the standard and think about what the compiler can do. If the compiler knows the definition of `A`, but not the definitions of `B` and `C`, what will the compiler do when deleting a pointer-to-`A`? – JaMiT Dec 08 '22 at 05:27
  • @JaMiT so when the static type is A and A doesn't have a virtual destructor no matter whether it's trivially destructible or not, it's UB? what about deleting a pointer-to-B? – iTruth Dec 08 '22 at 05:36
  • Does this answer your question? [Will using delete with a base class pointer cause a memory leak?](https://stackoverflow.com/questions/2100644/will-using-delete-with-a-base-class-pointer-cause-a-memory-leak) – JaMiT Dec 08 '22 at 05:36
  • Also related: [When to use virtual destructors?](https://stackoverflow.com/questions/461203/) [Is this undefined behavior or not?](https://stackoverflow.com/questions/10775247/) [Multiple inheritance and polymorphism](https://stackoverflow.com/questions/31796349/) – JaMiT Dec 08 '22 at 05:38
  • 3
    @iTruth Inventing irrelevant complications does not change what the standard says. Don't imagine it says more or less than it does. As you quoted "the static type shall have a virtual destructor or the behavior is undefined". Where does it mention something related to trivially destructible? Where does it say anything about base classes of the static class making a difference? Find the static class, look at its destructor, and you know all you need to know. – JaMiT Dec 08 '22 at 05:40
  • @JaMiT thank you for your answer, I have readed all the post you given. unfortunately, all of these posts doesn't answer my question. I think my problem is slightly different. as you know, the class A in code1 is a POD class, so that I can pass it directly to C code. but can I derived a class from A that I can use OOP? – iTruth Dec 08 '22 at 06:07
  • 2
    I don't understand what about JaMiT's answer doesn't answer your question, or what makes your situation "slightly different". POD doesn't matter. Deleting a `C` through a pointer to `A` when `A` doesn't have a virtual destructor is undefined behavior. If that fact interferes with something else you're trying to do, you might be better served asking a question that actually gets into what you're attempting. – Nathan Pierson Dec 08 '22 at 06:32
  • @NathanPierson not JaMiT's answer doesn't answer my question, I mean the link he post. he's answer has high reference value. for now I know deleting a C through a pointer to A when A doesn't have a virtual destructor is undefined behavior. but what about deleting a C through a pointer to B? – iTruth Dec 08 '22 at 06:40
  • If a type has a virtual destructor the compiler will call the right destructor for a pointer of that type even if the instance is really of a derived class. (Practically, it will pick the destructor function pointer from VMT.) If it doesn't have a virtual destructor it will destruct the type of the pointer. (No entry in VMT.) So: Q1: Yes Q2: No Q3: Yes Q4: No Q5: If any base class has a virtual destructor: No otherwise Yes. – Scheff's Cat Dec 08 '22 at 06:42
  • FYI: [demo on coliru](http://coliru.stacked-crooked.com/a/266185be0df26364) – Scheff's Cat Dec 08 '22 at 06:47

1 Answers1

2

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.

bitmask
  • 32,434
  • 14
  • 99
  • 159