C++中菱形继承的基本概念及内存占用问题

先上代码:

#include <iostream>
using namespace std;
class Base
{
public:
	int age = 10;
};

class Son1 : public Base
{
};

class Son2 : public Base
{
};

class Grandson : public Son1, public Son2
{
};

int main()
{
	Grandson g;
	//g.age = 40;	//报错,模糊调用
	g.Son1::age = 20;
	g.Son2::age = 30;
	cout << "g.Son1::age = " << g.Son1::age << endl;
	cout << "g.Son2::age = " << g.Son2::age << endl;
	system("pause");
	return 0;
}

首先,说一下什么是菱形继承。如上代码,两个类Son1和Son2都继承自Base,之后又创建了一个Grandson类,其有两个父类,都是继承自Base的Son1和Son2类。这就构成了菱形继承。
在这里插入图片描述
注: Son1和Son2下面简称父类,Base简称基类,Grandson简称子类。

然后,出现了两个问题

  1. main函数中注释行出现了错误,由于子类的两个父类都继承自Base,所以直接写 g.age=0; 会出现指向不明的错误,即二义性,因为两个父类中都有age这个参数,编译器不知道应该访问哪一个。

解决方法
访问属性的时候添加作用域。如: g.Son1::age = 20; g.Son2::age = 20; 。这样编译器就知道应该访问哪一个属性了。

  1. 一个子类对象中会出现两个age属性。但是,事实上一个age属性就已经足够了。出翔=现两个并没有什么实际用途,还会该访问带来麻烦,也浪费内存。

解决方法
虚继承。虚继承就是在父类继承基类的时候,在继承方式前加上 virtual 关键字。这样就可以解决该问题。程序如下:

#include <iostream>
using namespace std;

class Base
{
public:
	int age = 10;
};

class Son1 : virtual public Base
{
};
class Son2 : virtual public Base
{
};

class Grandson : public Son1, public Son2
{
};

int main()
{
	Grandson g;
	g.age = 40;
	g.Son1::age = 20;
	g.Son2::age = 30;
	cout << "g.age = " << g.age << endl;
	cout << "g.Son1::age = " << g.Son1::age << endl;
	cout << "g.Son2::age = " << g.Son2::age << endl;
	system("pause");
	return 0;
}

此时在子类的实例化对象g中就只有一个age属性了,所以也就可以直接使用 g.age 进行访问和修改了。运行结果如下:
在这里插入图片描述
由于仅有一个age属性,所以age的值是最后由最后的赋值语句 ==g.Son2::age = 30;==赋予的30。

然后,再看一下占用内存的问题。

我们通过vs2015开发人员命令提示符来查看一下Grandson类的内存布局:
在此之前,可以在看一下虚继承之前的内存布局:
在这里插入图片描述
接下来是虚继承之后的:
在这里插入图片描述
可以看到,子类的两个父类中并没有age属性,整个类中,有且仅有一个来自Base类的age属性。在两个父类中将各有一个名为 vbptr 的东西代替了属性age,其实这是一个指针,叫做 虚基类指针
每个虚基类指针都给各自指向了 vbtable 虚基类表。这个虚基类表中存储了虚继承的属性列表在内存中的偏移量。而偏移量的起始位置为对象的vbptr在内存中的位置。
在这里插入图片描述
将代码修改为如下模式:

#include <iostream>
using namespace std;

class Base
{
public:
	int age = 10;
	//string name = "Base";
	int high = 170;
};

class Son1 : virtual public Base
{
public:
	int A = 30;
};
class Son2 : virtual public Base
{
};

class Grandson : public Son1, public Son2
{
public:
	int height = 60;
};

int main()
{
	Grandson g;

	system("pause");
	return 0;
}

再次查看内存布局结果如下:
在这里插入图片描述
得出如下结论:

  1. 只有虚继承的属性才会存储在虚基类表中。因为Son1类中的属性A,不在虚基类表中。
  2. 由于虚基类表的存在,无论虚继承了多少属性,在每个父类中只占4个字节的内存,即一个vbptr指针所占的空间。
  3. 虚继承之后虚继承的属性在内存中有且仅有一份。
  4. 虚基类表并不占用类的内存.
  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值