C++对象模型-2

前言


前一篇主要介绍了C++对象的内部基本布局和三种对象模型,没有考虑到继承的情况。在各种继承情况下,对象的内存布局又有什么变化呢?接下来将分三篇从普通单继承,普通多继承,虚拟单继承,虚拟多继承,菱形普通继承,菱形虚拟继承等几种继承方式下分别探究下C++对象模型的特点

普通继承


基类


简单实现代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// Base 类: First Inheritance Level
class Base
{
public:
Base(int var = 10) : _base_var(var) {};
public:
virtual void Run(void)
{
std::cout << "Base Class Run()" << std::endl;
}
virtual void RunBase(void)
{
std::cout << "Base Class RunBase()" << std::endl;
}
private:
int _base_var;
};

Gcc查看内存布局

1
2
3
4
5
6
7
8
9
10
11
12
13
Vtable for Base
Base::_ZTV4Base: 4 entries
0 (int (*)(...))0
8 (int (*)(...))(& _ZTI4Base)
16 (int (*)(...))Base::Run
24 (int (*)(...))Base::RunBase

Class Base
size=16 align=8
base size=12 base align=8
Base (0x0x7f223aac30c0) 0
vptr=((& Base::_ZTV4Base) + 16)

可以看到基类的内存空间排序顺序为虚函数表在第一个位置,这样做是为了保证高效的获取虚函数地址(不需要偏移),提高效率,然后是成员变量,成员变量按声明顺序依次排列

内存布局的代码验证

1.测试代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
typedef void(*pFunc)(void); // 函数指针定义,后续使用到的函数指针均为此
void BaseTest(void)
{
Base b;
std::cout << "---------begin base object inner memory layout test-------" << std::endl;

std::cout << " object size is: " << sizeof(b) << std::endl;
std::cout << " object addr is: " << &b << std::endl; //
std::cout << " object vfptr value is: " << (long*)*(long*)&b << std::endl; //vfptr 在对象第1个位置 最外层(long*)是把第一个位置的值转换为long地址类型
std::cout << " typeid(b) is: " << typeid(b).name() << std::endl; // b对象类型
//std::cout << " object vfptr[-1] type info name is: " << ((type_info*)((long*)(*(long*)&b) - 1))->name() << std::endl;
std::cout << " object vfptr[0] func ptr is: " << (long*)*(long*)(*(long*)&b)<< std::endl; //vfptr[0]的值
std::cout << " object vfptr[0] func invoke res: ";
pFunc pRun = (pFunc)(*(long*)(*(long*)&b));
(*pRun)(); //调用vfptr[0](函数地址值)指向的函数
std::cout << " object vfptr[1] func ptr is: " << (long*)*((long*)(*(long*)&b) + 1) << std::endl; //vfptr[1]的值
std::cout << " object vfptr[1] func invoke res: ";
pFunc pRunBase = (pFunc)(*((long*)(*(long*)&b) + 1));
(*pRunBase)(); //调用vfptr[1](函数地址值)指向的函数
std::cout << " object _base_var addr is: " << (long*)&b + 1 << std::endl; //_base_var 在对象第2个位置
std::cout << " object _base_var value is: " << (int)*((long*)&b + 1) << std::endl; //_base_var 在对象第2个位置

std::cout << "---------end base object inner memory layout test-------" << std::endl;

}

&b为对象的地址;
*(long*)&b为对象内存第一个位置的值,即是虚函数表地址,因此需要强转为long型指针类型,因为表项也为指针类型(表项为函数地址指针类型,而指针视为long类型)
*((long*)(*(long*)&b) + n)为虚函数表第n(n从0开始)个表项的值(即虚函数地址值),需要强转为pFunc类型
*((long*)&b + m)为对象内存第m个位置的值,此处m = 1为变量_base_var的数据本身值

2.运行结果:

1
2
3
4
5
6
7
8
9
10
11
12
---------begin base object inner memory layout test-------
object size is: 16
object addr is: 0x7ffd9a7680e0
object vfptr value is: 0x559bc73a9ce0
typeid(b) is: 4Base
object vfptr[0] func ptr is: 0x559bc73a611a
object vfptr[0] func invoke res: Base Class Run()
object vfptr[1] func ptr is: 0x559bc73a6156
object vfptr[1] func invoke res: Base Class RunBase()
object _base_var addr is: 0x7ffd9a7680e8
object _base_var value is: 10
---------end base object inner memory layout test-------

3.内存布局示意图:
base

普通单继承的子类


简单实现代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// Single Common Derive: Second Inheritance Level
class Derive_Sin_Com : public Base
{
public:
Derive_Sin_Com(int var = 20) : Base(var), _derive_sin_com_var(var) {}
public:
virtual void Run(void) // override Base::Run()
{
std::cout << "Derive_Sin_Com Class Run()" << std::endl;
}
virtual void RunDerive_Sin_Com(void) // new virtual function
{
std::cout << "Derive_Sin_Com Class RunDerive_Sin_Com()" << std::endl;
}
private:
int _derive_sin_com_var;
};

Gcc查看内存布局

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Vtable for Derive_Sin_Com
Derive_Sin_Com::_ZTV14Derive_Sin_Com: 5 entries
0 (int (*)(...))0
8 (int (*)(...))(& _ZTI14Derive_Sin_Com)
16 (int (*)(...))Derive_Sin_Com::Run
24 (int (*)(...))Base::RunBase
32 (int (*)(...))Derive_Sin_Com::RunDerive_Sin_Com

Class Derive_Sin_Com
size=16 align=8
base size=16 base align=8
Derive_Sin_Com (0x0x7f223ab202d8) 0
vptr=((& Derive_Sin_Com::_ZTV14Derive_Sin_Com) + 16)
Base (0x0x7f223ab13480) 0
primary-for Derive_Sin_Com (0x0x7f223ab202d8)

可以看到普通单继承的子类的内存空间特点是基类的内存布局+子类的成员变量,基类的内存布局包括基类的虚函数表和基类的成员变量。子类的虚函数在继承而来的基类的虚函数表基础上进行替换(override),新增(子类新的虚函数),保持操作(没有override)

内存布局的代码验证

1.测试代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
void SingleCommonDeriveTest(void)
{
Derive_Sin_Com d;
std::cout << "---------begin single common derive object inner memory layout test-------" << std::endl;

std::cout << " object size is: " << sizeof(d) << std::endl;
std::cout << " object addr is: " << &d << std::endl; // d指针转换为long指针
std::cout << " object vfptr value is: " << (long*)*(long*)&d << std::endl; // vfptr地址
std::cout << " typeid(d) is: " << typeid(d).name() << std::endl; // d对象类型
std::cout << " object vfptr[0] func ptr is: " << (long*)*(long*)(*(long*)&d) << std::endl; //vfptr[0]的值
std::cout << " object vfptr[0] func invoke res: ";
pFunc pRun = (pFunc)(*(long*)(*(long*)&d));
(*pRun)(); //调用vfptr[0](函数地址值)指向的函数
std::cout << " object vfptr[1] func ptr is: " << (long*)*((long*)(*(long*)&d) + 1) << std::endl; //vfptr[1]的值
std::cout << " object vfptr[1] func invoke res: ";
pFunc pRunBase = (pFunc)(*((long*)(*(long*)&d) + 1));
(*pRunBase)(); //调用vfptr[1](函数地址值)指向的函数
std::cout << " object vfptr[2] func ptr is: " << (long*)*((long*)(*(long*)&d) + 2) << std::endl; //vfptr[2]的值
std::cout << " object vfptr[2] func invoke res: ";
pFunc pRunDerive_Sin_Com = (pFunc)(*((long*)(*(long*)&d) + 2));
(*pRunDerive_Sin_Com)(); //调用vfptr[2](函数地址值)指向的函数
std::cout << " object _base_var addr is: " << (long*)&d + 1 << std::endl; //_base_var 在对象第2个位置
std::cout << " object _base_var value is: " << *((int*)((long*)&d + 1) + 0) << std::endl;
std::cout << " object _derive_sin_com_var addr is: " << (long*)((int*)((long*)&d + 1) + 1) << std::endl; //_derive_sin_com_var 在对象第3个位置
std::cout << " object _derive_sin_com_var value is: " << *((int*)((long*)&d + 1) + 1) << std::endl;

std::cout << "---------end single common derive object inner memory layout test-------" << std::endl;
}

2.运行结果:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
---------begin single common derive object inner memory layout test-------
object size is: 16
object addr is: 0x7ffd9db3df50
object vfptr value is: 0x5606ab76b728
typeid(d) is: 14Derive_Sin_Com
object vfptr[0] func ptr is: 0x5606ab7671c8
object vfptr[0] func invoke res: Derive_Sin_Com Class Run()
object vfptr[1] func ptr is: 0x5606ab766d62
object vfptr[1] func invoke res: Base Class RunBase()
object vfptr[2] func ptr is: 0x5606ab767204
object vfptr[2] func invoke res: Derive_Sin_Com Class RunDerive_Sin_Com()
object _base_var addr is: 0x7ffd9db3df58
object _base_var value is: 20
object _derive_sin_com_var addr is: 0x7ffd9db3df5c
object _derive_sin_com_var value is: 20
---------end single common derive object inner memory layout test-------

3.内存布局示意图:
single common derive

普通多继承的子类


简单实现代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
// Base2 类 : First Inheritance Level
class Base2
{
public:
Base2(int var = 10) : _base2_var(var) {};
public:
virtual void Run(void)
{
std::cout << "Base2 Class Run()" << std::endl;
}
virtual void RunBase2(void)
{
std::cout << "Base2 Class RunBase2()" << std::endl;
}
private:
int _base2_var;
};

// Multi Common Derive: Second Inheritance Level
class Derive_Mul_Com : public Base, public Base2
{
public:
Derive_Mul_Com(int var = 30) : Base(var), Base2(var), _derive_mul_com_var(var) {}
public:
virtual void Run(void) // override Base::Run() and Base2::Run()
{
std::cout << "Derive_Mul_Com Class Run()" << std::endl;
}
virtual void RunBase(void) // override Base::RunBase()
{
std::cout << "Derive_Mul_Com Class RunBase()" << std::endl;
}
virtual void RunDerive_Mul_Com(void) // new virtual function
{
std::cout << "Derive_Mul_Com Class RunDerive_Mul_Com()" << std::endl;
}
private:
int _derive_mul_com_var;
};

Gcc查看内存布局

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
Vtable for Derive_Mul_Com
Derive_Mul_Com::_ZTV14Derive_Mul_Com: 9 entries
0 (int (*)(...))0
8 (int (*)(...))(& _ZTI14Derive_Mul_Com)
16 (int (*)(...))Derive_Mul_Com::Run
24 (int (*)(...))Derive_Mul_Com::RunBase
32 (int (*)(...))Derive_Mul_Com::RunDerive_Mul_Com
40 (int (*)(...))-16
48 (int (*)(...))(& _ZTI14Derive_Mul_Com)
56 (int (*)(...))Derive_Mul_Com::_ZThn16_N14Derive_Mul_Com3RunEv
64 (int (*)(...))Base2::RunBase2

Class Derive_Mul_Com
size=32 align=8
base size=32 base align=8
Derive_Mul_Com (0x0x7f223af5caf0) 0
vptr=((& Derive_Mul_Com::_ZTV14Derive_Mul_Com) + 16)
Base (0x0x7f223ab790c0) 0
primary-for Derive_Mul_Com (0x0x7f223af5caf0)
Base2 (0x0x7f223ab79120) 16
vptr=((& Derive_Mul_Com::_ZTV14Derive_Mul_Com) + 56)

可以看到普通多继承的子类的内存空间特点是基类的内存布局(按照继承的顺序依次布局基类)+子类的成员变量,基类的内存布局包括基类的虚函数表和基类的成员变量。子类的虚函数在继承而来的第一个继承的基类的虚函数表基础上进行替换(override),新增(子类新的虚函数),保持操作(没有override)

内存布局的代码验证

1.测试代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
void MultiCommonDeriveTest(void)
{
Derive_Mul_Com d;
std::cout << "---------begin multi common derive object inner memory layout test-------" << std::endl;

std::cout << " object size is: " << sizeof(d) << std::endl;
std::cout << " object addr is: " << &d << std::endl; // d指针转换为long指针
std::cout << " typeid(d) is: " << typeid(d).name() << std::endl; // d对象类型
std::cout << "------ base obj begin -----" << std::endl;
std::cout << " object base vfptr value is: " << (long*)*(long*)&d << std::endl; // base vfptr地址 在对象第1个位置
std::cout << " object base vfptr[0] func ptr is: " << (long*)*(long*)(*(long*)&d) << std::endl; //vfptr[0]的值
std::cout << " object base vfptr[0] func invoke res: ";
pFunc pRun = (pFunc)(*(long*)(*(long*)&d));
(*pRun)(); //调用vfptr[0](函数地址值)指向的函数
std::cout << " object base vfptr[1] func ptr is: " << (long*)*((long*)(*(long*)&d) + 1) << std::endl; //vfptr[1]的值
std::cout << " object base vfptr[1] func invoke res: ";
pFunc pRunBase = (pFunc)(*((long*)(*(long*)&d) + 1));
(*pRunBase)(); //调用vfptr[1](函数地址值)指向的函数
std::cout << " object base vfptr[2] func ptr is: " << (long*)*((long*)(*(long*)&d) + 2) << std::endl; //vfptr[2]的值
std::cout << " object base vfptr[2] func invoke res: ";
pFunc pRunDerive_Mul_Com = (pFunc)(*((long*)(*(long*)&d) + 2));
(*pRunDerive_Mul_Com)(); //调用vfptr[2](函数地址值)指向的函数
std::cout << " object _base_var addr is: " << (long*)&d + 1 << std::endl; //_base_var 在对象第2个位置
std::cout << " object _base_var value is: " << (int)*((long*)&d + 1) << std::endl;
std::cout << "------ base obj end -----" << std::endl;

std::cout << "------ base2 obj begin -----" << std::endl;
std::cout << " object base2 vfptr addr is: " << (long*)&d + 2 << std::endl; // base2 vfptr址 在对象第3个位置
std::cout << " object base2 vfptr value is: " << (long*)*((long*)&d + 2) << std::endl; // base2 vfptr值
std::cout << " object base2 vfptr[0] func ptr is: " << (long*)*(long*)(*((long*)&d + 2)) << std::endl; //vfptr[0]的值
std::cout << " object base2 vfptr[0] func invoke res: ";
pFunc pRun2 = (pFunc)(*(long*)(*((long*)&d + 2)));
(*pRun2)(); //调用vfptr[0](函数地址值)指向的函数
std::cout << " object base2 vfptr[1] func ptr is: " << (long*)*((long*)(*((long*)&d + 2)) + 1) << std::endl; //vfptr[1]的值
std::cout << " object base2 vfptr[1] func invoke res: ";
pFunc pRunBase2 = (pFunc)(*((long*)(*((long*)&d + 2)) + 1));
(*pRunBase2)(); //调用vfptr[1](函数地址值)指向的函数
std::cout << " object _base2_var addr is: " << (long*)&d + 3 << std::endl; //_base2_var 在对象第4个位置
std::cout << " object _base2_var value is: " << (int)*((long*)&d + 3) << std::endl;
std::cout << "------ base2 obj end -----" << std::endl;

std::cout << " object _derive_mul_com_var addr is: " << (long*)((int*)((long*)&d + 3) + 1) << std::endl; //_derive_mul_com_var 在对象第5个位置
std::cout << " object _derive_mul_com_var value is: " << *((int*)((long*)&d + 3) + 1) << std::endl;

std::cout << "---------end multi common derive object inner memory layout test-------" << std::endl;

}

2.运行结果:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
---------begin multi common derive object inner memory layout test-------
object size is: 32
object addr is: 0x7ffdcf415720
typeid(d) is: 14Derive_Mul_Com
------ base obj begin -----
object base vfptr value is: 0x558b051786e0
object base vfptr[0] func ptr is: 0x558b051742a4
object base vfptr[0] func invoke res: Derive_Mul_Com Class Run()
object base vfptr[1] func ptr is: 0x558b051742ea
object base vfptr[1] func invoke res: Derive_Mul_Com Class RunBase()
object base vfptr[2] func ptr is: 0x558b05174326
object base vfptr[2] func invoke res: Derive_Mul_Com Class RunDerive_Mul_Com()
object _base_var addr is: 0x7ffdcf415728
object _base_var value is: 30
------ base obj end -----
------ base2 obj begin -----
object base2 vfptr addr is: 0x7ffdcf415730
object base2 vfptr value is: 0x558b05178708
object base2 vfptr[0] func ptr is: 0x558b051742df
object base2 vfptr[0] func invoke res: Derive_Mul_Com Class Run()
object base2 vfptr[1] func ptr is: 0x558b05173e04
object base2 vfptr[1] func invoke res: Base2 Class RunBase2()
object _base2_var addr is: 0x7ffdcf415738
object _base2_var value is: 30
------ base2 obj end -----
object _derive_mul_com_var addr is: 0x7ffdcf41573c
object _derive_mul_com_var value is: 30
---------end multi common derive object inner memory layout test-------

3.内存布局示意图:
multi common derive

总结


本篇主要介绍了C++类在普通继承下的子类对象内存特点,可以看出,子类对象中的虚函数均是在继承而来的基类虚函数表中进行操作的,不会创建自己的虚函数表,子类的自己内存部分只是增加了自己的成员变量部分。对于多重情况下,子类会继承每一个基类的虚函数表,这就意味着子类在多重继承下会存在多张虚表