Introduction
This article talks about Virtual Table and _vptr in Replicated Multiple Inheritance and in Virtual Base Class (or Shared Multiple Inheritance).
Here it is explained using an example:
In this example, we have Base1, Base2, Base3 and Derive classes, where Base2 and Base3 classes are derived from Base1 class and Derive class is derived from Base2 and Base3 classes.
Background
Replicated Multiple Inheritance:
Here, Base2 and Base3 classes are derived Normally from Base1 class, there is a duplicate data and methods of Base1 class to Derive class (from Base2 and Base3 classes) and when we try to access that common data or functions through Derive object, there will be a ambiguity in accessing it.
As I have already explained in my last artcile (it is listed in reference part here), in Mupltile Inheritance, Derive classes (which has been derived from more than one base class) will have it's Base classe's VPTR with it. And it's own virtual functions will be listed as part of it's first Base classes virtual table.
For Replicated MI example, if we take teh object memory map for Derive class's object, functions and data of it's base classes will be listed as below:
vptr and data of class Base2 - contains virtual methods of Base1, Base2(self), then Derive class's ones, i.e.:
---------
Base1 class Virtual Methods
Base2 class virtual Methods
Base2 class data (if any)
Derive class virtual Methods
then
vptr and data of class Base3 - contains virtual methods of Base1 and Base3 (self):
---------
Base1 class Virtual Methods
Base3 class Virtual Methods
Base3 class Data (if any)
Derive data
------------
Derive class Data
Also Size of the Derive object would be:
_vptr of Base2 class + data of Base2 class
+ _vptr of Base3 class + data of Base3 class
+ data of Derive class
The same is explained here in this example code and o/p too is given:
Here for this example, we can not have Data for Base1 class and if we try to have, will get a compilation error (amgibuous) for Base1's member data.
If you, observe the memory map for object Derive d, the virtual functions of foremost base class i.e. Base1 class is duplicated in both the vtables of classes Base2 and Base3.
The code
Replicated Multiple Inheritance example
Code: Cpp
#include<iostream.h>
class Base1
{
public:
virtual void func1(void){cout<<"Base1::func1\n";};
};
class Base2 : public Base1
{
public:
int a;
virtual void func2(void){cout<<"Base2::func2\n";};
};
class Base3 : public Base1{
public:
virtual void func3(void){cout<<"Base3::func3\n";};
};
class Derive: public Base2, public Base3
{
public:
int b;
virtual void func4(void){cout<<"Derive::func4\n";};
};
typedef void (*fun) (void);
int main()
{
Derive d;
int* vptr;
fun funp=NULL;
cout<<"Size of Derive object d is = "<<sizeof(d)<<endl<<endl;
cout<< "Memory map of Derive object d :\n";
cout<<"------------------------------------\n\n";
// calling virtual function func1 of Base1
funp = (fun)*((int*)*(int*)((int*)&d+0)+0);
funp();
// calling virtual function func2 of Base2
funp = (fun)*((int*)*(int*)((int*)&d+0)+1);
funp();
// calling virtual function func4 of Derive
funp = (fun)*((int*)*(int*)((int*)&d+0)+2);
funp();
// accessing and changing the variable of Base2
int* pInt = (int*)&d+1;
*(pInt+0) = 30;
cout<<"Base2 class data: a =" << d.a <<endl;
// calling virtual function func1 of Base1
funp = (fun)*((int*)*(int*)((int*)&d+2)+0);
funp();
// calling virtual function func3 of Base3
funp = (fun)*((int*)*(int*)((int*)&d+2)+1);
funp();
// accessing and changing the variable of Derive
pInt = (int*)&d+3;
*(pInt+0) = 100;
cout<<"Derive class data: b =" << d.b <<endl;
return(0);
}
output
-----
Size of Derive object d is = 16
Memory map of Derive object d :
------------------------------------
Base1::func1
Base2::func2
Derive::func4
Base2 class data: a =30
Base1::func1
Base3::func3
Derive class data: b =100
So for the above code (size of Derive object "d" is):
4 bytes (_vptr of Base2) +
4 bytes (data i.e. int a of Base2 class) +
4 bytes (_vptr of Base3 class) +
4 bytes (data i.e. int b of Derive class)
that equates to 16
Here you can see the pictorial view of memory map for Derive class object

Shared Multiple Inheritance or Virtual Base class
As a solution for not to replicate the members and data of foremost base class, we go for Virtual Base classes.
So, for our above example, we need to make class Base1 as Virtual base class for class Base2 and class Base3, then derive class Derive from Base2 and Base3.
I have noticed 2 different memory-map for Derive object for Virual Base or Shared Multiple Inheritance as below:
1. When foremost base class has only virtual functions and no data to share.
2. When foremost base class has some data and/or virtual functions
For the first case, if we check the memory map for Derive class object, it is as below i.e:
vptr and data of Base2 class - contains foremost class i.e. Base1, Base2 and Derive class virtual methods:
----------
Base1 class Virtual Functions
Base2 class Virtual Functions
Derive class Virtual Functions
Base2 data
vptr and data of Base3 class - contains only Base3 virtual methods and contains
pointers to Base1 class (common class) virtual methods
------------
Pointers to virtual functions of Base1 class
Base3 class Virtual Methods
Base3 data
Data of Derive class
----------
Derive class data
Also the Size of the Derive object would be:
_vptr of Base2 class + data of Base2 class
+ _vptr of Base3 class + data of Base3 class
+ data of Derive class
The same is explained here in this example code and o/p too is given:
The code
Shared Mutiple Inheritance without data eample
Code: Cpp
#include<iostream.h>
class Base1
{
public:
virtual void func1(void){cout<<"Base1::func1\n";};
};
class Base2 : virtual public Base1
{
public:
int a;
virtual void func2(void){cout<<"Base2::func2\n";};
};
class Base3 : virtual public Base1{
public:
virtual void func3(void){cout<<"Base3::func3\n";};
};
class Derive: public Base2, public Base3
{
public:
int b;
virtual void func4(void){cout<<"Derive::func4\n";};
};
typedef void (*fun) (void);
int main()
{
Derive d;
fun funp=NULL;
cout<<"Size of Derive object d is = "<<sizeof(d)<<endl<<endl;
cout<< "Memory map of Derive object d :\n";
cout<<"------------------------------------\n\n";
// calling virtual function func1 of Base1
funp = (fun)*((int*)*(int*)((int*)&d+0)+0);
funp();
// calling virtual function func2 of Base2
funp = (fun)*((int*)*(int*)((int*)&d+0)+1);
funp();
// calling virtual function func4 of Derive
funp = (fun)*((int*)*(int*)((int*)&d+0)+2);
funp();
// accessing and changing the variable of Base2
int* pInt = (int*)&d+1;
*(pInt+0) = 30;
cout<<"Base2 class Data: a =" << d.a <<endl;
// calling virtual function func3 of Base3
funp = (fun)*((int*)*(int*)((int*)&d+2)+1);
funp();
cout<<"Base3 class Data if any (there is no data for Base3 class here in example)" <<endl;
// accessing and changing the variable of Derive
pInt = (int*)&d+3;
*(pInt+0) = 100;
cout<<"Derive class Data: b =" << d.b <<endl<<endl;
return(0);
}
output
-----
Size of Derive object d is = 16
Memory map of Derive object d :
------------------------------------
Base1::func1
Base2::func2
Derive::func4
Base2 class Data: a =30
Base3::func3
Base3 class Data if any (there is no data for Base3 class here in example)
Derive class Data: b =100
So for the above code (size of Derive object "d" is):
4 bytes (_vptr of Base2) +
4 bytes (data i.e. int a of Base2 class) +
4 bytes (_vptr of Base3 class) +
4 bytes (data i.e. int b of Derive class)
that equates to 16
Here you can see the pictorial view of memory map for Derive class object
For the second case i.e. if foremost class contains some data along with/without virtual functions, then object memory of Derive class object will be as shown below:
vptr and data of Base2 class - contains only Base2 (self) and Derive class virtual methods:
----------
Base2 class Virtual Functions
Derive class Virtual Functions
Base2 data
vptr and data of Base3 class - contains only Base3 virtual methods
------------
Base3 class Virtual Methods
Base3 data
Data of Derive object and vptr of foremost Base class and it's data
----------
Derive class data
virtual functions of foremost class i.e. Base1 class
Data of foremost class i.e. Base1 class
Also Size of the Derive object would be:
_vptr of Base2 class + data of Base2 class +
_vptr of Base3 class + data of Base3 class +
data of Derive class +
_vptr of Base1 class + data of Base1 class
The same is explained here in this example code and o/p too is given:
The code
Shared Multiple Inheritance with Data
Code: Cpp
#include<iostream.h>
class Base1
{
public:
virtual void func1(void){cout<<"Base1::func1\n";};
int a;
};
class Base2 : virtual public Base1
{
public:
int b;
virtual void func2(void){cout<<"Base2::func2\n";};
};
class Base3 : virtual public Base1{
public:
virtual void func3(void){cout<<"Base3::func3\n";};
};
class Derive: public Base2, public Base3
{
public:
int c;
virtual void func4(void){cout<<"Derive::func4\n";};
};
typedef void (*fun) (void);
int main()
{
Derive d;
fun funp=NULL;
cout<<"Size of Derive object d is = "<<sizeof(d)<<endl<<endl;
cout<< "Memory map of Derive object d :\n";
cout<<"------------------------------------\n\n";
// calling virtual function func1 of Base2
funp = (fun)*((int*)*(int*)((int*)&d+0)+0);
funp();
// calling virtual function func4 of Derive
funp = (fun)*((int*)*(int*)((int*)&d+0)+1);
funp();
// accessing and changing the variable of Base2
int* pInt = (int*)&d+1;
*(pInt+0) = 30;
cout<<"Base2 class Data: b =" << d.b <<endl;
// calling virtual function func3 of Base3
funp = (fun)*((int*)*(int*)((int*)&d+2)+0);
funp();
cout<<"Base3 class Data if any (there is no data for Base3 class here in example)" <<endl;
// accessing and changing the variable of Derive
pInt = (int*)&d+3;
*(pInt+0) = 60;
cout<<"Derive class Data: c =" << d.c <<endl;
// calling virtual function func1 of Base1
funp = (fun)*((int*)*(int*)((int*)&d+4)+0);
funp();
// accessing and changing the variable of Base1
pInt = (int*)&d+5;
*(pInt+0) = 120;
cout<<"Base1 class Data: a =" << d.a <<endl<<endl;
return(0);
}
output
-----
solutionWithData.cc
Size of Derive object d is = 24
Memory map of Derive object d :
------------------------------------
Base2::func2
Derive::func4
Base2 class Data: b =30
Base3::func3
Base3 class Data if any (there is no data for Base3 class here in example)
Derive class Data: c =60
Base1::func1
Base1 class Data: a =120
So for the above code (size of Derive object "d" is):
4 bytes (_vptr of Base2 class) +
4 bytes (data i.e. int b of Base2 class) +
4 bytes (_vptr of Base3 class) +
4 bytes (data i.e. int c of Derive class)
4 bytes (_vptr of Base1 class) +
4 bytes (data i.e. int a of Base1 class)
that eqautes to 24
If you notice here, the size of the Derive object differs in both the cases as explained above.
Here you can see the pictorial view of memory map for Derive class object
References
How Virtual Table and _vptr works
How Virtual Table and _vptr works
Virtual Table and _vptr in Multiple Inheritance
Virtual Table and _vptr in Multiple Inheritance


