虚拟继承与菱形虚拟继承

虚拟继承

什么是虚拟继承?
虚拟继承是C++编程语言中的一种语法,使得派生类如果继承基类多次,只有一份基类的拷贝在派生类对象中。需在在继承方式前加上virtual。

#include
using namespace std;

class B
{
public:
    int _b;
};

class D:virtual public B
{
public:
    int _d;
};

int main()
{
    D d;
    d._b = 1;
    d._d = 2;
    return 0;
}

这是一个简单的单虚拟继承。相关赋值后我们去内存看看派生类对象的模型。

这里写图片描述
我们看到第二行存放的是_d的值,最后一行存放的是_b的值。那么第一行这一串数据是什么呢?这其实是一个地址(偏移量表的地址),我们去这个地址看看里面存放了些什么。
这里写图片描述
这其中第一行的0代表着偏移量表相对于自己的偏移量,为0。第二行的8代表着偏移量表相对于基类的偏移量,为8。

单虚拟继承派生类的空间模型

虚拟继承与菱形虚拟继承_第1张图片

我们去看看反汇编代码

这里写图片描述

我们发现在调用D构造函数之前有push 1这样的代码,这其实是虚拟继承的标志。

派生类D的构造函数到底做了什么,要访问基类成员应该怎么访问?
偏移量表的地址由派生类D的构造函数填写,要想访问基类成员,首先要去找偏移量表,之后根据与基类的偏移量进行访问。

菱形虚拟继承

回想菱形继承,一个派生类D继承了基类C1,C2。C1与C2同时继承了相同的基类B。当我们直接由派生类对象d对基类B的成员进行操作时,会出现二义性的问题。因为编译器不知道你所说的基类B是C1所继承的还是C2所继承的。
虚拟继承完美的解决了这个问题。 派生类D如果继承了多次基类,只有一份基类的拷贝在派生类内。

问题来了,虚拟继承应该发生在那个地方?
这个问题很简单,如果发生在派生类D多继承C1,C2时,C1,C2已经同时继承了基类B,届时派生类D中已经存在了两份基类B的拷贝。因此虚拟继承应该发生在C1,C2继承基类B时。
虚拟继承与菱形虚拟继承_第2张图片

#include
using namespace std;

class B
{
public:
    int _b;
};

class C1:virtual public B
{
public:
    int _c1;
};
class C2:virtual public B
{
public:
    int _c2;
};
class D:public C1,public C2
{
public:
    int _d;
};

int main()
{
    D d;
    d._b = 1;
    d._c1 = 2;
    d._c2 = 3;
    d._d = 4;
    return 0;
}

同样我们看看派生类d在内存中的模型。
这里写图片描述
首先我们看到第二行存放_c1的值,第四行存放_c2的值,接着就是_d,在最底部存放_b。同样这其中存放了两个偏移量表的地址。我们去分别看看两张偏移量表有什么不同。
先去0x0129580c看看
这里写图片描述
同样第一行是偏移量表相对自己的偏移量为0。第二行是偏移量表相对基类B的偏移量为14(十六进制,十进制为20)。

之后去0x01295800看看
0x01295800
第一行是偏移量表相对自己的偏移量为0。第二行是偏移量表相对基类B的偏移量为0c(十六进制,十进制为12)。

我们可以得出派生类对象d的模型
虚拟继承与菱形虚拟继承_第3张图片
我们可以看到派生类对象内部只存放了一份基类拷贝,这时对基类成员_b进行赋值时不会出现二义性问题。

你可能感兴趣的:(c++)