C + + inheritance in detail three--Diamond inheritance + Virtual inheritance Memory object Model VBPTR (1)

Source: Internet
Author: User

In my personal study of the process of inheritance, in the online access to a lot of information, the knowledge of diamond inheritance is added to the virtual function, that is involved in the problem of polymorphism, and I did not learn at that time polymorphic this piece, so see a lot of information is foggy, Then this article I want to learn from my own experience in the process, from Jane to the more difficult to analyze the following diamond inheritance, so that beginners first to the problem has a little concept, in the back will be easy to continue to analyze.
This article does not refer to polymorphism is the virtual function of the diamond inheritance, in the later article updates, I will slowly add these things.
Diamond Inheritance (also known as Diamond Inheritance) is the following scenario:

The corresponding code is as follows:

#include <iostream>using namespace STD;classb{ Public: B () {cout<<"B"<< Endl; } ~b () {cout<<"~b ()"<< Endl; }Private:intb;};classC1: Publicb{ Public: C1 () {cout<<"C1 ()"<< Endl; } ~c1 () {cout<<"~c1 ()"<< Endl; }Private:intC1;};classC2: Publicb{ Public: C2 () {cout<<"C2 ()"<< Endl; } ~c2 () {cout<<"~c2 ()"<< Endl; }Private:intC2;};classD: PublicC1, PublicE2= Public: D () {cout<<"D ()"<< Endl; } ~d () {cout<<"~d ()"<< Endl; }Private:intD;};intMain () {cout<<sizeof(B) << Endl;cout<<sizeof(C1) << Endl;cout<<sizeof(C2) << Endl;cout<<sizeof(D) << Endl;return 0;}

The result of the operation is:

We want the object model of Class D in the above code to look like this:

In fact, the model of Class D in the code above is

Diamond inheritance can cause data redundancy in derived classes, such as in the example above where the class D contains two int B.
In order to solve the problem of diamond inherited data redundancy, I want to introduce the concept of virtual inheritance.
1. Virtual Inheritance
Virtual inheritance is a technique in object-oriented programming, which refers to a specified base class that, in an inheritance architecture, shares its member data instances with other classes that derive directly or indirectly from this base type.
Virtual inheritance is a unique concept in multiple inheritance. The virtual base class appears to resolve multiple inheritance.
We can see the difference between virtual base class and non-virtual base class in multiple inheritance

So why introduce virtual inheritance?
We have analyzed the object model of the derived class from the multiple inheritance of the general non-virtual base class, then see what the following code will output

#include <iostream>using namespace STD;classb{ Public: B () {cout<<"B"<< Endl; } ~b () {cout<<"~b ()"<< Endl; }intb;};classC1: Publicb{ Public: C1 () {cout<<"C1 ()"<< Endl; } ~c1 () {cout<<"~c1 ()"<< Endl; }Private:intC1;};classC2: Publicb{ Public: C2 () {cout<<"C2 ()"<< Endl; } ~c2 () {cout<<"~c2 ()"<< Endl; }Private:intC2;};classB | PublicC1, Publicc2{ Public: D () {cout<<"D ()"<< Endl; } ~d () {cout<<"~d ()"<< Endl; }voidFuntest () {b =Ten; }Private:intD;};intMain () {D D; D.funtest ();return 0;}

Compile error, output

Error   1   error C2385: ambiguous access of ‘b‘    e:\demo\继承\blog\project1\project1\source.cpp    58  1   Project1    2   IntelliSense: "D::b" is ambiguous   e:\DEMO\继承\blog\Project1\Project1\Source.cpp    58  3   Project1

Compiler error is: Ambiguous B, that is, the compiler can not tell whether B is inherited from C1 or inherited from C2.
To solve the above problem of two semantics and data redundancy due to diamond inheritance, virtual inheritance is needed.
Virtual inheritance is proposed to solve multiple inheritance, it is possible to save two copies of the problem, that is, with the virtual inheritance of only one copy is retained, but this copy is shared by the multiple inheritance of the base class, how to implement this mechanism?
Let me take a step-by-stage analysis of this problem:
1. Do not add data members to the class
Look at the following code:

#include <iostream>using namespace STD;classB//base class{ Public: B () {cout<<"B"<< Endl; } ~b () {cout<<"~b ()"<< Endl; }};classC1:Virtual  Publicb{ Public: C1 () {cout<<"C1 ()"<< Endl; } ~c1 () {cout<<"~c1 ()"<< Endl; }};classC2:Virtual  Publicb{ Public: C2 () {cout<<"C2 ()"<< Endl; } ~c2 () {cout<<"~c2 ()"<< Endl; }};classB | PublicC1, Publicc2{ Public: D () {cout<<"D ()"<< Endl; } ~d () {cout<<"~d ()"<< Endl; }};intMain () {cout<<sizeof(B) << Endl;cout<<sizeof(C1) << Endl;cout<<sizeof(C2) << Endl;cout<<sizeof(D) << Endl;return 0;}

The output is:

Let's analyze the results:

class//基类{public:    B()    {        cout"B" << endl;    }    ~B()    {        cout"~B()" << endl;    }};

First, there are no other members in the base class except constructors and destructors, so

sizeof(B) = 1;

Some beginners may ask why 1, the first class in memory storage is this:
If there is a class B

class B{    public:    int b;    void fun();};int Test(){    B b1,b2,b3;}

Then in-memory models such as

Therefore the member functions are stored separately, and all class objects are common.
Then someone might say that sizeof (B) Why not 0, that is because the compiler to give the object an address, it is necessary to partition all the class object, 1 is a placeholder, indicating that the object exists, and let the compiler assign an address to this object.
Now the problem with sizeof (B) is resolved, see below C1 and C2

class C1 :virtualpublic B{public:    C1()    {        "C1()" << endl;    }    ~C1()    {        "~C1()" << endl;    }};class C2 :virtualpublic B{public:    C2()    {        "C2()" << endl;    }    ~C2()    {        "~C2()" << endl;    }};

Since both C1 and C2 are virtual inheritance, a vbptr is stored at the beginning of the C1,C2 memory as a pointer to the virtual base class table.
So what does this pointer vbptr point to?
We generate a C1 class object in the main function C1

int main(){    C1 c1;    return0;}

See in memory what the C1 actually saved

As we can see, C1 occupies four bytes, and a pointer variable is stored, and the content of the pointer variable is the address of the virtual base class table that the C1 vbptr points to.
Let's go to the virtual base class table that c1.vbptr points to to see what's in store.


As you can see, this virtual base class table has eight bytes, which are stored in 0 and 4, respectively.
So what do 0 and 4 represent?
The Virtual base class table holds two offset addresses, 0 and 4, respectively.
where 0 represents the offset of the C1 object address relative to the address holding the vptr pointer
Can be expressed in &c1->vbptr_c1.
Where vptr refers to a pointer to a virtual table, and the virtual table is defined by the virtual function only, because we do not define a virtual function here, of course, there is no vptr pointer, so the offset is 0.
8 represents the offset of the base class object part of the C1 object relative to the address where the VBPTR pointer is held
Can be expressed in &c1 (b)-&vbpt, where &C1 (b) Represents the address of the base class B part of the object C1.
The memory layout of C2 is the same as C1, because c1,c2 are virtual inherited from the B base class and C1,C2 have no data members.

Now everyone is right

sizeof4;sizeof4;

There's no doubt about it.
To summarize, because C1,C2 is virtual inherited from base class B, the compiler will generate a pointer to C1,C2 Vbptr point to a virtual base class table, that is, the value of the pointer vbptr is the address of the virtual base class table.
The offset is stored in this virtual base class table.
This table is divided into two parts, and the first part stores the offset of the object relative to the Vptr pointer, which can be represented by the & (object name)->vbptr_ (object name). For C1 objects, you can use &C1->VBPRT_C1 to represent them.
The vptr pointer is a pointer to a virtual table, and only the virtual function is defined in the class, because the virtual function is not defined in this example, so there is no vptr pointer, so the first part of the offset is 0.
The second part of the table stores the offset of the base class object part of the object relative to the address that holds the vbptr pointer, and we know that in this case the base class object and the pointer offset are the size of the pointer.

What does D actually save in memory?

As shown, D has two pointers stored in memory, and we enter the address of the pointer to see what it is:

As shown, there are two virtual base class pointers stored in D, with offsets in each virtual base class table.
It's too abstract to say so much.
Now look at the memory layout:

2. Adding data members to the class
Above, we analyzed the diamond inheritance without data members, the following analysis of the addition of data members, so that you can more clearly see the memory layout

#include <iostream>using namespace STD;classb{ Public: B () {cout<<"B"<< Endl; } ~b () {cout<<"~b ()"<< Endl; }intb;};classC1:Virtual  Publicb{ Public: C1 () {cout<<"C1 ()"<< Endl; } ~c1 () {cout<<"~c1 ()"<< Endl; }intC1;};classC2:Virtual  Publicb{ Public: C2 () {cout<<"C2 ()"<< Endl; } ~c2 () {cout<<"~c2 ()"<< Endl; }intC2;};classB | PublicC1, Publicc2{ Public: D () {cout<<"D ()"<< Endl; } ~d () {cout<<"~d ()"<< Endl; }voidFun () {b =0; C1 =1; C2 =2; D =3; }intD;};intMain () {cout<<sizeof(B) << Endl;cout<<sizeof(C1) << Endl;cout<<sizeof(C2) << Endl;cout<<sizeof(D) << Endl;    D D; D.fun ();return 0;}

This time the output is:

This time we'll dissect the output sizes of each class.

class C1 :virtualpublic B{public:    C1()    {        "C1()" << endl;    }    ~C1()    {        "~C1()" << endl;    }    int c1;};

First of all the c1,c2 are virtual inherited from the base class B, so I will analyze together.
First, B takes up four bytes, because the B class has an int b data member, so Class B occupies four bytes.
Then C1,C2 is virtual inherit from Class B, so c1,c2 memory layout is similar, here I only dissect C1.
We add a fun member function to the C1 class to see the memory layout more clearly

class C1 :virtualpublic B{public:    C1()    {        "C1()" << endl;    }    ~C1()    {        "~C1()" << endl;    }    void Fun()    {        5;        6;    }    int c1;};int main(){    C1 c1;    c1.Fun();    return0;}

What is the C1 in memory when the object C1 is generated in the main function?

We enter the virtual base class table of C1 through the vbptr pointer

By the above two figure we can draw the C1 memory layout

C2 is like C1.
So

sizeof12;sizeof12;

Now let's look at the memory layout of Class D.

class D: public  C1, public  c2{public : d  () {    cout <<  "D ()"  << Endl;    } ~d () {cout <<  "~d ()"  << Endl;  } void  Fun () //fun () function mainly helps us to see the memory layout of class D  {b = 0 ; //base class data member  c1 = 1 ; //C1 class data member  c2 = 2 ; //C2 class data member  d = 3 ;    //d class own data member } int  D;};  

We go into memory D

As you can see, the first four bytes are the vbptr pointer, then the C1 class, and the other vbptr pointer +c2 class + D class data member d+ base class B structure
Let's go into the first vbptr pointer.

The offsets are 0 (because there is no virtual function), and 14
And then into the second vbptr pointer.

You can see that the offsets are 0 (because there is no virtual function), and 12
Well, here we can draw the memory layout of Class D.

So

sizeof(D) == 24;

This is the diamond inheritance without virtual function, about the diamond inheritance with virtual function because it involves the knowledge of polymorphism, I put it in the back to write an article specifically.
If you have any questions, please comment on the private messages.
More in-depth ideas are also welcome to discuss.

C + + inheritance in detail three--Diamond inheritance + Virtual inheritance Memory object Model VBPTR (1)

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.