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 函数,则通过虚函数表+虚指针来支持:
-
每个类生成一个表格,称为虚表(virtual table,简称vtble)。虚表中存放着一堆指针,这些指针指向该类每一个虚函数。虚表中的函数地址将按声明时的顺序排列
-
每个类对象都拥有一个虚表指针(vptr),由编译器为其生成。虚表指针的设定与重置皆由类的复制控制(也即是构造函数、析构函数、赋值操作符)来完成。vptr的位置为编译器决定,传统上它被放在所有显示声明的成员之后,不过现在许多编译器把vptr放在一个类对象的最前端(也就是说对象的地址就是vptr的地址)
-
虚函数表的前面设置了一个指向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的地址
文章到此结束!!!欢迎指正!