Virtual Table and _vptr in Replicated and Shared Multiple Inheritance

Discussion in 'C++' started by Mridula, Apr 3, 2009.

  1. Mridula

    Mridula New Member

    Joined:
    Mar 5, 2008
    Messages:
    316
    Likes Received:
    19
    Trophy Points:
    0
    Occupation:
    S/w proffessional
    Location:
    Bangalore

    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:
    
    #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


    [​IMG]

    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:
    
    #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


    [​IMG]

    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:
    
    #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

    [​IMG]


    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
     
    Last edited by a moderator: Jan 21, 2017
  2. asadullah.ansari

    asadullah.ansari TechCake

    Joined:
    Jan 9, 2008
    Messages:
    356
    Likes Received:
    14
    Trophy Points:
    0
    Occupation:
    Developer
    Location:
    NOIDA
    Extremely nice article...Keep posting such nice atricles...
     
  3. Mridula

    Mridula New Member

    Joined:
    Mar 5, 2008
    Messages:
    316
    Likes Received:
    19
    Trophy Points:
    0
    Occupation:
    S/w proffessional
    Location:
    Bangalore
    If you like it so much, please vote for me later.

    many thanks
    Mridula.
     
  4. shabbir

    shabbir Administrator Staff Member

    Joined:
    Jul 12, 2004
    Messages:
    15,375
    Likes Received:
    388
    Trophy Points:
    83
  5. shabbir

    shabbir Administrator Staff Member

    Joined:
    Jul 12, 2004
    Messages:
    15,375
    Likes Received:
    388
    Trophy Points:
    83

Share This Page

  1. This site uses cookies to help personalise content, tailor your experience and to keep you logged in if you register.
    By continuing to use this site, you are consenting to our use of cookies.
    Dismiss Notice