4

There are couple of questions regarding this.But I am still not clear about this. Consider this multiple inheritance.

class Base1 
{ 
   public: 
     Base1(); 
     virtual ~Base1(); 
     virtual void speakClearly(); 
     virtual Base1 *clone() const; 
   protected: 
     float data_Base1; 
}; 

class Base2 
{ 
   public: 
     Base2(); 
     virtual ~Base2(); 
     virtual void mumble(); 
     virtual Base2 *clone() const; 
   protected: 
     float data_Base2; 
}; 

class Derived : public Base1, public Base2 
{ 
   public: 
     Derived(); 
     virtual ~Derived(); 
     virtual Derived *clone() const; 
   protected: 
     float data_Derived; 
}; 

and consider these two statements

Base1 *pbase1 = new Derived; 
Base2 *pbase2 = new Derived;

The book inside the C++ Object model states that in one of the optimization.

The vtable of pbase1 subobject and Derived share the same vtable. Exact phrase in the book

"Base 1 being leftmost, it already points to the beginning of the Derived class object "

How does this happen? I understand this will change if I change the order of inheritance as

class Derived : public Base2, public Base1

But I dont understand how this is done by the compiler. Can anybody explain how pbase1 and derived share the same v_table?

user1429322
  • 1,266
  • 2
  • 24
  • 38
  • Being v_tables usually implemented as pointer offsets in an array (but doesn't need to be), I just guess that they can share the v_table for objects of class `Derived` because the compiler can merge `Base1` and `Base2`v_tables at compile-time. What should be strange about that? – Germán Diago Oct 02 '13 at 08:23

2 Answers2

2

It's all very implementation defined, but typically, a compiler will lay out Derived as

Base1: vptr
Base1: data
Base2: vptr
Base2: data
Derived: data

The address of the Derived is the address of the whole, thus the same physical address as that of Base1. The initial part of the vtable for Derived will be identical with that of Base1, and the Base1: vptr will in fact point to the vtable of Derived.

If you inverse Base1 and Base2 in the declaration, the compiler will inverse their roles above. (Typically. There's nothing in the standard which would prevent the compiler from arranging the bases in alphabetical order, or putting the derived data first, but I've never heard of a compiler which did so.)

James Kanze
  • 150,581
  • 18
  • 184
  • 329
1

Think to a class as having some "hideen members" that preceed the ones you declare, giving a layout like this one:

  • The vtable pointer
  • The fisrt base
    • It's vtable pointer
    • It's own members
  • The second base
    • It's vtable pointer
    • It's own members
  • The derived members

Now the problem is "where should the pointers point to?"

Since all the functions are available in the most derived object, you can create an "array of function pointers" for:

  • dtor,
  • speakClearly
  • clone
  • mumble

This table works the same for Derived and Base1 (it has just to ignore the last line), but doesn't for Base2, that has to see just

  • dtor
  • clone
  • mumble

with a different indexing than the previous (that's why it needs to be different).

Because of this fact, there is no need to distinguish between the Derived and first-base table instances (Of course, we can do the same reasoning starting from the last base and differentiating the previous ones ... but we have to start somewhere)

The compiler will hence "compact" the implementation, by creating just a table having the first base virtual functions plus all the others, and other distinct tables for the other bases, than it will initialize all the pointer to the most derived implementation the the virtual functions.

The layout will be

  1. Derived vtable
  2. Base1 members
  3. Base2 versus Derived vtable
  4. Base2 members
  5. Derived members

Now, new Derived will just create that sketch, and:

  • If given to Derived* will make it point to 1.
  • If given to Base1* will make it point to 1. (jusk like Derived, but "shorter")
  • If given to Base2* will make it point to 3.
Emilio Garavaglia
  • 20,229
  • 2
  • 46
  • 63