2

Does this code result in defined behavior?

class A {
    int x;
};
class B {
    short y;
};
class C {
    double z;
};

class D : public A, public B, public C {
    float bouncy;
};

void deleteB(B *b) {
    delete b;
}

void is_it_defined() {
    D *d = new D;
    deleteB(d);

    B *b = new D;  // Is this any different?
    delete b;
}

If it's not defined, why not? And if it is, what's it defined to do and why? Lastly, if it's implementation defined, could you give an example of what a common implementation might define the behavior to be?

Omnifarious
  • 54,333
  • 19
  • 131
  • 194
  • possible duplicate of [Does delete work with pointers to base class?](http://stackoverflow.com/questions/294927/does-delete-work-with-pointers-to-base-class) – Björn Pollex Nov 14 '10 at 18:32

3 Answers3

6

Quoting Herb Sutter :

If deletion can be performed polymorphically through the base class interface, then it must behave virtually and must be virtual. Indeed, the language requires it - if you delete polymorphically without a virtual destructor, you summon the dreaded specter of "undefined behavior".

In your example, both delete are performed through base class pointers and yield undefined behavior. Standard 5.3.5 (Delete) :

In the first alternative (delete object), if the static type of the operand is different from its dynamic type, the static type shall be a base class of the operand’s dynamic type and the static type shall have a virtual destructor or the behavior is undefined.

Here, both delete act on static type B while the operand's dynamic type is D.

icecrime
  • 74,451
  • 13
  • 99
  • 111
  • The problem here is that non of the classes is polymorphic. `B *b = new D;` stores the pointer of `B` inside the newly allocated `D`. And the delete will fail if `B` has a non-zero offset inside `D`. What the heck is undefined here? – Šimon Tóth Nov 14 '10 at 18:34
  • Just note that in this example, the dynamic type of `b` is `B*` because `B` is not polymorphic. – Šimon Tóth Nov 14 '10 at 18:35
  • @Let_Me-Be The dynamic type of b is D*. – Sjoerd Nov 14 '10 at 18:40
  • Indeed, none of the clases is polymorphic. Yet, the code as it is written expects deletion to behave virtually. – icecrime Nov 14 '10 at 18:42
  • @Sjoerd Yeah, and I guess you expect `typeid(*b)` to return `D`? – Šimon Tóth Nov 14 '10 at 18:44
  • @icecrime Yes the code is wrong, there is no dispute in that. What we are talking about is if it is defined/undefined/implementation defined. – Šimon Tóth Nov 14 '10 at 18:45
  • @Let_Me_Be No, I expect typeid(*b) == typeid(B), because class B has no virtual members. Typeid(*b) will not return the dynamic type in this case. – Sjoerd Nov 14 '10 at 18:46
  • @Let_Me_Be: the code as presented violates a "Shall" (5.3.5, the static type shall have a virtual destructor) and therefore has undefined behavior. There's no need for dispute or discussion. Similarly, Sjoerd is right on all other points. – MSalters Nov 15 '10 at 11:37
  • @MSalters If the dynamic type is different from static type. I have already posted this into comp.std.c++ (waiting for response). I still claim that the dynamic and static type of `b` is `B*` because the `class B` is not polymorphic. – Šimon Tóth Nov 15 '10 at 12:13
  • @Let_Me_Be *1.3.3 dynamic type : the type of the most derived object (1.8) to which the lvalue denoted by an lvalue expression refers*. Sorry, I don't see how the class being polymorphic has anything to do with it. – icecrime Nov 15 '10 at 12:15
  • @icecrime Well, the `lvalue` doesn't refer to `D*` but `B*`, because in case of non-polymorphic classes there is no information about the `class D` preserved. This type is discarded in the assignment. You could say that even if there is no information about this, the compound memory block is still `D*`, well, what about placement new then? In that case, the compound block will be most likely `char*`. It's a wording issue, and that's why I asked for clarification in `comp.std.c++`. – Šimon Tóth Nov 15 '10 at 12:24
  • @Let_Me_Be The fact that compilers don't record the information in case of non-polymorphic classes, doesn't matter at all. The C++ standard states that the dynamic type of b is D* (see the quotes digged up by icecrime), and that's all that counts. – Sjoerd Nov 15 '10 at 13:36
  • @Sjoerd - I'm wondering what way of not being wrong our contrarian is going to find when the comp.std.c++ people tell h(im/er) that (s)he's wrong too. :-) – Omnifarious Nov 15 '10 at 16:44
0

B doesn't have a virtual destructor, it should have.

peoro
  • 25,562
  • 20
  • 98
  • 150
-1

is it all concern about the virtual destructor? look at this:

class A 
{
    int x;

public:
    virtual void fun()
    {
        return;
    }
};

class D : public A 
{
    float bouncy;
};

void is_it_defined()
{

    A *b = new D;  // it`s ok!
    delete b;
}

u see? it`s ok. The pointer b can be delete correctly. So, just need a vitual function to activate the polymorphic.

Omnifarious
  • 54,333
  • 19
  • 131
  • 194
  • -1 for saying something that isn't correct. You __do__ need a virtual destructor. Not just any old virtual function. I know you need a virtual destructor in this case, but my question isn't how to make it work properly. My question is what happens when you don't do the right thing? Is the result predictable in any way? – Omnifarious Nov 15 '10 at 06:19
  • Of course, i need a virtual destructor to make resource be released correctly. But i have a question, why i add a virtual function, the delete operation is OK. I still confused by the object model. So i just post a question to discuss. – lj_enjoylife Nov 15 '10 at 06:56
  • C++ doesn't work that way. It's a very powerful language, but that doesn't come for free. One of the downsides of C++ is that incorrect code may seem to work. Your code is a clear example. The code is wrong, but your compiler couldn't check at compile time and didn't bother to check at runtime. We know it's wrong because the standard specifically says it's wrong (see icecrime's response) – MSalters Nov 16 '10 at 09:43