对象内存解析:单继承、多继承、菱形继承

C++对象模型:

参考:https://www.cnblogs.com/raichen/p/5744300.html

类中成员分类

数据成员分为静态和非静态,成员函数有静态非静态以及虚函数

class data members:static和nonstatic

class data functions:static、nonstatic和virtual

在此模型下,nonstatic 数据成员被置于每一个类对象中,而static数据成员被置于类对象之外。static与nonstatic函数也都放在类对象之外,而对于virtual 函数,则通过虚函数表+虚指针来支持:

  1. 每个类生成一个表格,称为虚表(virtual table,简称vtble)。虚表中存放着一堆指针,这些指针指向该类每一个虚函数。虚表中的函数地址将按声明时的顺序排列

  2. 每个类对象都拥有一个虚表指针(vptr),由编译器为其生成。虚表指针的设定与重置皆由类的复制控制(也即是构造函数、析构函数、赋值操作符)来完成。vptr的位置为编译器决定,传统上它被放在所有显示声明的成员之后,不过现在许多编译器把vptr放在一个类对象的最前端(也就是说对象的地址就是vptr的地址)

  3. 虚函数表的前面设置了一个指向type_info的指针,用以支持RTTI(Run Time Type Identification,运行时类型识别)。RTTI是为多态而生成的信息,包括对象继承关系,对象本身的描述等,只有具有虚函数的对象在会生成。

单继承、多继承、菱形继承概念区别:

单继承:类只继承自一个父亲

多继承:类继承自多个父亲

菱形继承:是一种特殊的多继承

单继承分析:

例子:

类Sheep只有一个父亲,他是单继承

内存对象分析:

可以看到单继承会继承父类的所有权限的类成员。

多继承:

例子:

类Der3继承自两个父亲:Der1、Der2,其内存模型:

补充:虚函数/变量表存放的不是函数地址,而是一个跳转的指令的地址【代码段

通过vbptr指向的vbtable,找到对应的变量跳转地址,再找到变量的保存地址,操作变量

菱形继承:一种特殊的多继承

场景:一个类Animal有两个继承者Sheep、Horse,再有一个类Lv继承了这两者,则Lv会有两个一模一样的相同变量m_A

class Animal
{
public:
	int m_A;

};

 class Sheep:public Animal
{
public:
	Sheep() {}
};


class Horse :public Animal
{
public:
	Horse()
	{

	}
};


class Lv :public Sheep, public Horse
{
public:
	Lv(int a)
	{
		m_A = 100;
	}
};

Lv内存对象:

拥有两个m_A,程序怎么知道使用哪一个?

虚继承的解决:

通过在继承时加上virtual关键字

解决菱形继承的原理分析:

通过加上virtual关键字后的类内存对象:

通过int型对 类地址取值,找到 vbptr 表所在地址,通过表格地址,取出里面的 vbptr偏移量,从而找到m_A

使用:可以通过Lv首地址 ,找到vbptr表,再取出vbptr的偏移量,用Lv的首地址(=继承的sheep首地址) + 偏移量取出, Lv对象的m_A的值   

因为Lv 定义的对象里面都会有两个继承的个 vbptr指针,通过这两个指针都可以访问m_A

从表中可以看出:

 sheep 首地址 + 相对的偏移量(此时为8)=m_A地址

 Horse首地址  + 相对的偏移量 (此时为4)=m_A地址

验证:可以通过指针来取值

void test02()
{
	Sheep s;
	Horse h;
	Lv my(10);
	//使用virtual之后,多了两个指针,指向m_A地址
	//使用指针通过 偏移量来访问 m_A

	//求出 虚拟指针表内 sheep继承的指针相对于m_A的偏移量
	int s_offset=*((int *)*(int *)&my + 1);//??
	//取出m_A
	int a = *(int *)((char*)&my + s_offset);//100
	cout << a << endl;
	//cout << (int*)&my << endl;
	//cout << (int *)*(int *)&my << endl;

	//求出 虚拟指针表内 horse继承的指针相对于m_A的偏移量
	int h_offset = *((int*)*(int*)&my + 3);
	cout << h_offset << endl;
	int b = *(int*)((char*)((int *)&my+1) + h_offset);
	cout << b << endl;
}

int main()
{
	test02();
	system("pause");
	return 0;
}

程序结果:

通过vs2015对内存进行分析,会发现vbptr保存的是m_A的相对于对象基地址的偏移量,需要加上对象的基地址,才是变量m_A的地址

 

文章到此结束!!!欢迎指正!

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值