简体   繁体   中英

C++ order of specifying base classes in derived class

This may be a basic question but I haven't seen it anywhere. In C++, suppose I have the following code:

#include <iostream>

using namespace std;

class B  {
public:
    void f();
};

class C  {
public:
    void f();
};

class D : public B, public C{
public:
    void f();
};

void B::f(){cout << "bbb" << endl;}
void C::f(){cout << "ccc" << endl;}
void D::f(){cout << "ddd" << endl;}

int main () {

B *d = new D;

delete d;
d->f();
return 0;
}

This works fine and outputs "bbb" . But if I were to switch the order of

class D : public B, public C{

to

class D : public C, public B{

I would get a "Aborted (core dumped)" error. What is the reason for this? Could someone further explain in what ways the order of the base classes matters?

I know this is multiple inheritance but I am avoiding the diamond problem so not sure what's going on.

The class B is not polymorphic (eg does not provide any virtual methods). Hence the compiler assumes that that the dynamic type of the object that d points to is B , and it tries to delete memory based on the address of the that B object, which in no longer matches the address of the actual D object. It is as one would write:

D * d = new D;
delete static_cast<B *>(d);

Undefined behavior right there. Why it seems to work when the order of classes inherited by D is B , C , is that probably in your case the address of the B part of the D object happened to be identical to the address of the D object itself. There are no destructors (explicitly or implicitly) defined, so only a deallocation function was called, which probably accepted the pointer provided by the respective allocation function.

Note also that according to [basic.life]

Similarly, before the lifetime of an object has started but after the storage which the object will occupy has been allocated or, after the lifetime of an object has ended and before the storage which the object occupied is reused or released, any glvalue that refers to the original object may be used but only in limited ways. For an object under construction or destruction, see [class.cdtor]. Otherwise, such a glvalue refers to allocated storage ([basic.stc.dynamic.deallocation]), and using the properties of the glvalue that do not depend on its value is well-defined. The program has undefined behavior if:

  • the glvalue is used to access the object, or
  • the glvalue is used to call a non-static member function of the object, or
  • the glvalue is bound to a reference to a virtual base class ([dcl.init.ref]), or
  • the glvalue is used as the operand of a dynamic_cast or as the operand of typeid.

Therefore calling d->f(); after delete d; in your code also causes undefined behavior.

It's undefined behaviour , pure and simple.

You can't use d once you've delete d it. Granted it's odd that it "works" one way and not the other, but that's the nature of undefined behaviour . (You can conject the reason by noting that in the first way the address of D is the address of B - note that your classes are not polymorphic types - , but in the second case it isn't. And the delete is merely releasing the memory, not immediately wiping it).

But really,

delete d;
d->f();

is not going to end well. Even if you do swap the order of those statements, you ought to make the base class destructors virtual so the correct memory is deleted.

class D : public B, public C and class D : public C, public B , amongst other things, exchanges the order in which the base classes are constructed when D is constructed.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM