Virtual Table and _vptr in Replicated and Shared Multiple Inheritance

Mridula's Avatar author of Virtual Table and _vptr in Replicated and Shared Multiple Inheritance
This is an article on Virtual Table and _vptr in Replicated and Shared Multiple Inheritance in C++.

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
0
asadullah.ansari's Avatar, Join Date: Jan 2008
TechCake
Extremely nice article...Keep posting such nice atricles...
0
Mridula's Avatar, Join Date: Mar 2008
Pro contributor
If you like it so much, please vote for me later.

many thanks
Mridula.
0
0
shabbir's Avatar, Join Date: Jul 2004
Go4Expert Founder
Start Voting for article of the month - April 2009