Search code examples
c++multiple-inheritancevtablevirtual-inheritancememory-layout

Object layout in case of virtual functions and multiple inheritance


I was recently asked in an interview about object layout with virtual functions and multiple inheritance involved.
I explained it in context of how it is implemented without multiple inheritance involved (i.e. how the compiler generated the virtual table, insert a secret pointer to the virtual table in each object and so on).
It seemed to me that there was something missing in my explanation.
So here are questions (see example below)

  1. What is the exact memory layout of the object of class C.
  2. Virtual tables entries for class C.
  3. Sizes (as returned by sizeof) of object of classes A, B and C. (8, 8, 16 ?? )
  4. What if virtual inheritance is used. Surely the sizes and virtual table entries should be affected ?

Example code:

class A {  
  public:   
    virtual int funA();     
  private:  
    int a;  
};

class B {  
  public:  
    virtual int funB();  
  private:  
    int b;  
};  

class C : public A, public B {  
  private:  
    int c;  
};   

Thanks!


Solution

  • The memory layout and the vtable layout depend on your compiler. Using my gcc for instance, they look like this:

    sizeof(int) == 4
    sizeof(A) == 8
    sizeof(B) == 8
    sizeof(C) == 20
    

    Note that sizeof(int) and the space needed for the vtable pointer can also vary from compiler to compiler and platform to platform. The reason why sizeof(C) == 20 and not 16 is that gcc gives it 8 bytes for the A subobject, 8 bytes for the B subobject and 4 bytes for its member int c.

    Vtable for C
    C::_ZTV1C: 6u entries
    0     (int (*)(...))0
    4     (int (*)(...))(& _ZTI1C)
    8     A::funA
    12    (int (*)(...))-0x00000000000000008
    16    (int (*)(...))(& _ZTI1C)
    20    B::funB
    
    Class C
       size=20 align=4
       base size=20 base align=4
    C (0x40bd5e00) 0
        vptr=((& C::_ZTV1C) + 8u)
      A (0x40bd6080) 0
          primary-for C (0x40bd5e00)
      B (0x40bd60c0) 8
          vptr=((& C::_ZTV1C) + 20u)
    

    Using virtual inheritance

    class C : public virtual A, public virtual B
    

    the layout changes to

    Vtable for C
    C::_ZTV1C: 12u entries
    0     16u
    4     8u
    8     (int (*)(...))0
    12    (int (*)(...))(& _ZTI1C)
    16    0u
    20    (int (*)(...))-0x00000000000000008
    24    (int (*)(...))(& _ZTI1C)
    28    A::funA
    32    0u
    36    (int (*)(...))-0x00000000000000010
    40    (int (*)(...))(& _ZTI1C)
    44    B::funB
    
    VTT for C
    C::_ZTT1C: 3u entries
    0     ((& C::_ZTV1C) + 16u)
    4     ((& C::_ZTV1C) + 28u)
    8     ((& C::_ZTV1C) + 44u)
    
    Class C
       size=24 align=4
       base size=8 base align=4
    C (0x40bd5e00) 0
        vptridx=0u vptr=((& C::_ZTV1C) + 16u)
      A (0x40bd6080) 8 virtual
          vptridx=4u vbaseoffset=-0x0000000000000000c vptr=((& C::_ZTV1C) + 28u)
      B (0x40bd60c0) 16 virtual
          vptridx=8u vbaseoffset=-0x00000000000000010 vptr=((& C::_ZTV1C) + 44u)
    
    

    Using gcc, you can add -fdump-class-hierarchy to obtain this information.