Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How virtual inheritance is implemented in memory by c++ compiler?

I am baffled by the VIRTUAL keyword. I am trying to find how does compiler implement this in memory. ok so let me explain with examples. I am using Microsoft Visual studio 2010 as implementation of virtual depends on compiler.

here is the first code

#include<iostream>
class one
{
    int _a;
public:
    virtual ~one(){}
};
class two:public one
{
    int _a;
public:
    virtual ~two(){}
};

int main()
{
    using namespace std;
    cout<<"sizeof two="<<sizeof(two)<<endl;
    return 0;
}

o/p is 12 bytes, because of _vptr_two,one::_a and two::_a

here is another example code

#include<iostream>
class one
{
    int _a;
public:
    virtual ~one(){}
};
class two
{
    int _a;
public:
    virtual ~two(){}
};
class three:virtual public one,virtual public two
{
};

int main()
{
    using namespace std;
    cout<<"sizeof three="<<sizeof(three)<<endl;
    return 0;
}

in this case o/p is 20 bytes :O, how come?? Please explain!! According to me it should be 16 bytes. __vptr_three(pointer to vtable), _vptr1_three(pointer to virtual base class table), one::_a and two::_a. And why virtual base class table is maintained ?

like image 691
Amit Bhaira Avatar asked Jan 11 '23 04:01

Amit Bhaira


1 Answers

This pdf contains everything that you need to understand about, how virtual inheritance is implemented in VC++, by the writer of the compiler.

Below is the disassembly for the constructor of class three,

00A516BD  cmp         dword ptr [ebp+8],0 
00A516C1  je          three::three+60h (0A516F0h) 
00A516C3  mov         eax,dword ptr [this] 
00A516C6  mov         dword ptr [eax],offset three::`vbtable' (0A57828h) => 4 Bytes
00A516CC  mov         ecx,dword ptr [this] 
00A516CF  add         ecx,8 
00A516D2  call        one::one (0A51253h) 
00A516D7  or          dword ptr [ebp-0D4h],1 
00A516DE  mov         ecx,dword ptr [this] 
00A516E1  add         ecx,10h 
00A516E4  call        two::two (0A512BCh) 
00A516E9  or          dword ptr [ebp-0D4h],2 
00A516F0  mov         eax,dword ptr [this] 
00A516F3  mov         ecx,dword ptr [eax] 
00A516F5  mov         edx,dword ptr [ecx+4] 
00A516F8  mov         eax,dword ptr [this] 
00A516FB  mov         dword ptr [eax+edx],offset three::`vftable' (0A57820h)  => 4 Bytes
00A51702  mov         eax,dword ptr [this] 
00A51705  mov         ecx,dword ptr [eax] 
00A51707  mov         edx,dword ptr [ecx+8] 
00A5170A  mov         eax,dword ptr [this] 
00A5170D  mov         dword ptr [eax+edx],offset three::`vftable' (0A57814h)   => 4 Bytes
00A51714  mov         eax,dword ptr [this] 
00A51717  pop         edi  
00A51718  pop         esi  
00A51719  pop         ebx  
00A5171A  add         esp,0D8h 

As you can see above,
The virtual base table pointer takes 4 bytes, (vbtable)
2 virtual function table pointer takes 4 * 2 = 8 bytes, (vftable)
The member one::_a takes 4 bytes
The member two::_a takes 4 bytes

Hence in all 20 bytes. The reason for 2 virtual function table pointers is given in the pdf (page 17) as follows,

"In Visual C++, to avoid costly conversions to the virtual base P when fetching a vftable entry, new virtual functions of T, receive entries in a new vftable, requiring a new vfptr, introduced at the top of T."

like image 92
51k Avatar answered Jan 27 '23 13:01

51k