3

Given the following code:

namespace Example1 {

class A {
public:
    A() {}
    virtual ~A() {}
private:
    float data_A;
};

class B {
public:
    B() {}
    virtual ~B() {}
protected:
    float data_B;
};

class Derived : public A, public B {
public:
    Derived() {}
    virtual ~Derived() {}
protected:
    float data_Derived;
};

}

int main (void)
{
using namespace Example1;
B* pb = new Derived;
delete pb;
}

pb should now point to the B part of the Derived object. But the derived object also derives from A, means it has A sub-object.. and that A sub-object should be "first" because the Derived class first inherits from A.

How does the compiler approves that? what does it add in order to make it work correctly?

and also, how does it free the memory correctly when deleting the object?

4

6 回答 6

7

The short answer is: By magic.

The medium answer is: It's not for you to worry about. The Standard says that this works, and it's up to the compiler to figure out a way to make it work.

The long answer: Since this depends on your compiler, read up your compiler's documentation! Many C++ compilers implement the Itanium C++ ABI, so that's a start. As part of polymorphic inheritance, each class usually has a so-called vtable, which stores a bunch of function pointers, but it also stores RTTI information and joined virtual-destruction and memory-deallocation logic. Think about it: delete pb; doesn't just have to call the right destructors in the right order, but it also has to pass the correct pointer to the deallocation function. All this information is included in the various vtables of the class hierarchy.

于 2013-04-24T07:40:58.840 回答
0

Paragraph §10.1/2

[Note: The order of derivation is not significant except as specified by the semantics of initialization by constructor (12.6.2), cleanup (12.4), and storage layout (9.2, 11.1). —end note ]

therefore

class Derived : public A, public B {
                ^^^^^^^^  ^^^^^^^^
                  first    second

Then calls derived constructor. Also, destructors will invoked in reverse order.

Construction
A()
B()
D()

Destruction
~D()
~B()
~A()
于 2013-04-24T07:37:12.387 回答
0

It doesn't matter, which class you type first in derived class declaration. "A should be first" is not correct, as they are equally base classes. The only difference is, which constructor / destructor is called first (in the order / reversed order they were declared as base classes).

You can refer to why multiple inheritance is a bad idea

Perhaps what could be well suited for you is single inheritance, with B class deriving from A and C deriving from B.

于 2013-04-24T07:43:04.610 回答
0

Source from a french site : C++ FAQ

Les constructeurs sont appelés dans l'ordre suivant :

le constructeur des classes de base héritées virtuellement en profondeur croissante et de gauche à droite ;
le constructeur des classes de base héritées non virtuellement en profondeur croissante et de gauche à droite ;
le constructeur des membres dans l'ordre de leur déclaration ;
le constructeur de la classe.

Order of constructors' calls :

base class constructors from virtual inheritance (BFS style and from left to rigth);
base class constructors from non virtual inheritance (BFS style and from left to rigth);
instances constructor in the order of their declaration;
Class constructor

I'm guessing the destructors' calls order is the reverse of this order.

于 2013-04-24T07:43:41.727 回答
0

Since your destructors are (correctly) virtual here, the compiler has no problem at all. It just calls the right destructor (~Derived() in this case) and everything works fine.

The standard does not force any implementation, but a popular one used in many computers is a virtual table. All objects that belong to a class with at least one virtual function (as is the case here) have a pointer (vptr) to the virtual table (vtbl) corresponding to their class. This vtbl has references to the correct overrides of all virtual functions for the object class. Here I am talking about dynamic class (i.e.: the real class of the object), not static class (the class of the pointer). In this case:

  • The dynamic type of pb is Derived, since it points to a Derived instance
  • The static type of pb is B, since it was declared as B*

As you can see, the compiler does not care about the static type in this case. It interprets the instruction delete pb as this (pseudocode):

pb->vptr->destructor();
operator delete(pb); // Not exactly, but this is immaterial in this case

You can disregard the second line for our purposes. The runtime will go through this chain and find the right destructor to call (Derived()). Derived() will in turn call ~A() and ~B() in the right order.

于 2013-04-24T07:49:35.093 回答
0

The general answer is that it works, but the actual implementation is compiler dependant and you shouldn't rely on the details (but it is still a good thing to keep in mind so you don't make incorrect assumptions when dealing with pointers).

When multiple inheritance is used, simple line such as B* pb = new Derived implicitly changes the actual value of the pointer. In this particular case, since the compiler knows it has to convert a Derived pointer to B*, it knows exactly by how much it needs to change the pointer (e.g. sizeof(A), of course the actual value is probably different).

If you are using virtual inheritance (which is guarantees that a common base class is only included once, e.g. if both A and B inherited from CommonBase), then simple pointer conversion becomes even more complex, and the compiler does a vtable lookup to find the actual offset it should use for pointer conversion.

If you are using Visual Studio, you can make a breakpoint on this line and press Alt+8 to see the disassembly, which will reveal the 'magic' behind pointer conversion.

于 2013-04-24T07:58:16.947 回答