深度探索C++对象模型(15)——数据语义学(5)——虚基类初探
引入虚基类前:
代码:
#include <iostream>
#include <stdio.h>
using namespace std;
class Grand //爷爷类
{
public:
int m_grand;
};
class A1 : public Grand
{
public:
int m_a1;
};
class A2 : public Grand
{
public:
int m_a2;
};
class C1 :public A1, public A2
{
public:
int m_c1;
};
int main()
{
cout << sizeof(Grand) << endl;
cout << sizeof(A1) << endl;
cout << sizeof(A2) << endl;
cout << sizeof(C1) << endl;
C1 c1;
//c1.m_grand = 12; //访问不明确,名字冲突,二义性;
//引入虚基类之后,就不会出现访问不明确的问题了
c1.A1::m_grand = 13;
c1.A2::m_grand = 15;
c1.m_a1 = 1;
c1.m_a2 = 2;
c1.m_c1 = 3;
A1 a1;
a1.m_a1 = 10;
a1.m_grand = 11;
}
继承简图:
A1,A2各继承一份Grand类的成员,C1继承A1,A2的同时继承两份Grand
运行结果:
没有虚基类会引起二义性问题,即不指明调用哪个父类继承的爷爷类成员时,会出现访问不明确的现象
只能通过以下方式指明调用哪个父类继承的爷爷类成员
内存详情查看:
对象c1:
对象a1:
可以发现此处正常继承,Grand类子对象的成员在子类对象内存的前面
内存布局:
对象c1:
传统多重继承造成的 :空间问题(空间的浪费),效率问题,二义性问题(需要指明调用哪个父类继承的爷爷类的成员);
引入虚基类后:
虚基类解决的问题是爷爷类被多个类继承然后这多个父类又被子类继承,而引起的孙子类出现多次爷爷类成员的情况,即解决的是三层结构多重继承的问题
代码:
#include <iostream>
#include <stdio.h>
using namespace std;
class Grand //爷爷类
{
public:
int m_grand;
};
class A1 : virtual public Grand
{
public:
int m_a1;
};
class A2 : virtual public Grand
{
public:
int m_a2;
};
class C1 :public A1, public A2
{
public:
int m_c1;
};
int main()
{
cout << sizeof(Grand) << endl;
cout << sizeof(A1) << endl;
cout << sizeof(A2) << endl;
cout << sizeof(C1) << endl;
C1 c1;
c1.m_grand = 12; //引入虚基类之后,就不会出现访问不明确的问题了
c1.A1::m_grand = 13;
c1.A2::m_grand = 15;
c1.m_a1 = 1;
c1.m_a2 = 2;
c1.m_c1 = 3;
A1 a1;
a1.m_a1 = 10;
a1.m_grand = 11;
}
继承简图:
引入虚基类之后可以理解为C1中继承的Grand既不来自于A1也不来自于A2,而是Grand单独的一份拷贝
运行结果:
解释:
虚继承类A1:m_a1占4各字节、继承的m_grand占用4各字节、虚基类表指针占用4个字节
A2类似
类C1除过数据m_a1,m_a2,m_c1,m_grand本身,还有继承的两个虚基类表指针
反汇编:
可以发现引入了虚基类之后这三句赋值语句是等价的,因为只有一个m_grand,所以后面的赋值都会覆盖前面的,所以最终对象c1中的m_grand的值为15
内存详情查看:
对象c1:
对象a1:
内存布局:
虚基类A1(A2与之类似):
类C1:
可以发现对于虚基类Grand的成员是放在对象的最末尾,即VS2017下虚基类子对象被编译器放在继承它的子类对象的末尾内存