1.普通继承和虚拟继承区别
普通继承 | 虚拟继承 | |
继承列表是否加virtual | 不需要 | 需要 |
对象模型不同 | 基类部分在上,子类部分 在下 | 基类部分在下,子类部分在上,并且对象中多了四个字节 |
子类是否生成默认构造 | 不一定 如果基类有无参或者全缺省的构造方法子类没实现任何构造方法,则编译器一定会生成,如果基类没有定义任何构造方法,则编译器不生成 | 一定会生成 并且编译器在生成的构造方法中会填充对象前四个字节中的内容 |
子类对象访问基类成员方式 | 直接访问 | 简介访问:需要借助对象前四个字节中保存的指向偏移量表格的指针,找到对应的偏移量,然后再去访问基类部分的成员 |
(1)普通继承方式代码示例:
class B
{
public:
int _b;
};
class D:public B
{
public:
int _d;
};
int main()
{
cout << sizeof(D) << endl; // 8
D d;
d._b = 1;
d._d = 2;
return 0;
}
代码底层展示的结果:
子类访问基类成员的方式 : 直接访问
d._b =1; 对应的汇编 mov dword ptr [d] ,1
(2)虚拟继承的代码展示:
class B
{
public:
int _b;
};
class D :virtual public B
{
public:
int _d;
};
int main()
{
cout << sizeof(D) << endl; //普通继承的结果是8 虚拟继承会多四个字节 变成12字节
D d;
d._b = 1;
d._d = 2;//赋值时候直接一条mov指令搞定
return 0;
}
其中02 00 00 00对应的就是 子类新增部分的赋值
01 00 00 00 就是基类新增部分的赋值
对于子类新增部分:
赋值时候直接一条mov指令就搞定了。
基类部分:
给基类对象赋值时用到了三条mov指令:
第一条指令是:获取对象中前4个字节的内容:即虚基表(偏移量表格)的地址
第二条指令是:从虚基表中获取子类对象中基类部分成员存储的偏移量
第三条指令是:从基类对象位置往后偏移 offset字节,找到基类对应成员并复制
由上图知:子类对象从起始位置往后偏移8个字节,刚好是从基类部分继承下来成员存储的位置
右图也可以被称为虚基表。
由此可知多出的4个字节:这四个字节存储的是指向虚基表(偏移量表格的地址)
注意:虚拟继承只有一个作用,就是用来解决菱形继承二义性问题的---》菱形虚拟继承