C++对象模型(4)

前言


前两篇主要介绍了C++对象在一级继承体系下的对象模型,接下来将考虑二级继承体系下常用的菱形继承对象模型的影响和变化,主要包括普通菱形继承和虚拟菱形继承

菱形继承


普通菱形继承类


公共基类简单实现代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class B
{
public:
B(int var = 10): _b(var){};
~B() {}
public:
virtual void Run(void)
{
std::cout << "B Run()" << std::endl;
}
virtual void RunB(void)
{
std::cout << "B RunB()" << std::endl;
}
private:
int _b;
};

普通菱形继承类简单实现代码

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
48
49
50
51
52
53
54
class D1 : public B
{
public:
D1(int var = 20): B(var),_d1(var) {};
~D1() {}
public:
virtual void Run(void)
{
std::cout << "D1 Run()" << std::endl;
}
virtual void RunD1(void)
{
std::cout << "D1 RunD1()" << std::endl;
}
private:
int _d1;
};
class D2 : public B
{
public:
D2(int var = 20) : B(var), _d2(var) {};
~D2() {}
public:
virtual void Run(void)
{
std::cout << "D2 Run()" << std::endl;
}
virtual void RunD2(void)
{
std::cout << "D2 RunD2()" << std::endl;
}
private:
int _d2;
};
class Rhombus : public D1, public D2
{
public:
Rhombus(int var = 30) : D1(var), D2(var), _rhombus(var) {};
~Rhombus() {}
public:
virtual void Run(void)
{
std::cout << "Rhombus Run()" << std::endl;
}
virtual void RunRhombus(void)
{
std::cout << "Rhombus RunRhombus()" << std::endl;
}
private:
int _rhombus;
};

cl命令的内存布局

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
class Rhombus size(56):
+---
0 | +--- (base class D1) // 基类D1内存布局
0 | | +--- (base class B) // D1的公共基类B内存布局
0 | | | {vfptr}
8 | | | _b
| | | <alignment member> (size=4)
| | +---
16 | | _d1
| | <alignment member> (size=4)
| +---
24 | +--- (base class D2) // 基类D2内存布局
24 | | +--- (base class B) // D2的公共基类B内存布局
24 | | | {vfptr}
32 | | | _b
| | | <alignment member> (size=4)
| | +---
40 | | _d2
| | <alignment member> (size=4)
| +---
48 | _rhombus // 新加的成员变量
| <alignment member> (size=4)
+---
Rhombus::$vftable@D1@: // 基类D1的虚函数表
| &Rhombus_meta
| 0
0 | &Rhombus::Run // override: 子类重写的虚函数
1 | &B::RunB // 从公共基类B继承而来的虚函数
2 | &D1::RunD1 // 从基类D1继承而来的虚函数
3 | &Rhombus::RunRhombus // 子类新加的虚函数
Rhombus::$vftable@D2@: // 基类D2的虚函数表
| -24
0 | &thunk: this-=24; goto Rhombus::Run // override: 子类重写的虚函数
1 | &B::RunB // 从公共基类B继承而来的虚函数
2 | &D2::RunD2 // 从基类D2继承而来的虚函数
Rhombus::Run this adjustor: 0
Rhombus::RunRhombus this adjustor: 0

可以看到普通菱形继承类的内存空间特点是基类Di的内存布局(按照继承的顺序依次布局基类)+子类的成员变量,基类Di的内存布局包括公共基类B的内存布局和基类Di的成员变量。子类的虚函数在继承而来的第一个继承的Di基类的虚函数表基础上进行替换(override),新增子类(新的虚函数),保持操作(没有override)。可以看到普通菱形继承内存中存在多份公共基类B的结构,这样存在二义性的问题,如对于Rhombus类调用Run方法,由于两个子类D1,D2都重写了,且都继承来了,不知道调用哪个子类的方法

内存布局的代码验证

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
48
49
50
51
52
53
54
55
56
57
58
59
60
61
void RhombusCommonDeriveTest(void)
{
Rhombus d;
std::cout << "---------begin rhombus 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指针转换为int指针
std::cout << " typeid(d) is: " << typeid(d).name() << std::endl; // d对象类型
std::cout << "------ d1 obj begin -----" << std::endl;
std::cout << " object d1 vfptr addr is: " << (int*)*(int*)&d << std::endl; // d1 vfptr地址 在对象第1个位置
std::cout << " object d1 vfptr[0] func ptr is: " << (int*)*(int*)(*(int*)&d) << std::endl; //vfptr[0]的值
std::cout << " object d1 vfptr[0] func invoke res: ";
pFunc pRun = (pFunc)(*(int*)(*(int*)&d));
(*pRun)(); //调用vfptr[0](函数地址值)指向的函数
std::cout << " object d1 vfptr[1] func ptr is: " << (int*)*((int*)(*(int*)&d) + 1) << std::endl; //vfptr[1]的值
std::cout << " object d1 vfptr[1] func invoke res: ";
pFunc pRunB = (pFunc)(*((int*)(*(int*)&d) + 1));
(*pRunB)(); //调用vfptr[1](函数地址值)指向的函数
std::cout << " object d1 vfptr[2] func ptr is: " << (int*)*((int*)(*(int*)&d) + 2) << std::endl; //vfptr[2]的值
std::cout << " object d1 vfptr[2] func invoke res: ";
pFunc pRunD1 = (pFunc)(*((int*)(*(int*)&d) + 2));
(*pRunD1)(); //调用vfptr[2](函数地址值)指向的函数
std::cout << " object d1 vfptr[3] func ptr is: " << (int*)*((int*)(*(int*)&d) + 3) << std::endl; //vfptr[3]的值
std::cout << " object d1 vfptr[3] func invoke res: ";
pFunc pRunRhombus = (pFunc)(*((int*)(*(int*)&d) + 3));
(*pRunRhombus)(); //调用vfptr[3](函数地址值)指向的函数
std::cout << " object _b addr is: " << (int*)&d + 1 << std::endl; //_b 在对象第2个位置
std::cout << " object _b value is: " << *((int*)&d + 1) << std::endl;
std::cout << " object _d1 addr is: " << (int*)&d + 2 << std::endl; //_d1 在对象第3个位置
std::cout << " object _d1 value is: " << *((int*)&d + 2) << std::endl;
std::cout << "------ d1 obj end -----" << std::endl;
std::cout << "------ d2 obj begin -----" << std::endl;
std::cout << " object d2 vfptr addr is: " << (int*)*((int*)&d + 3) << std::endl; // d2 vfptr地址 在对象第4个位置
std::cout << " object d2 vfptr[0] func ptr is: " << (int*)*(int*)(*((int*)&d + 3)) << std::endl; //vfptr[0]的值
std::cout << " object d2 vfptr[0] func invoke res: ";
pFunc pRun2 = (pFunc)(*(int*)(*((int*)&d + 3)));
(*pRun2)(); //调用vfptr[0](函数地址值)指向的函数
std::cout << " object d2 vfptr[1] func ptr is: " << (int*)*((int*)(*((int*)&d + 3)) + 1) << std::endl; //vfptr[1]的值
std::cout << " object d2 vfptr[1] func invoke res: ";
pFunc pRunB2 = (pFunc)(*((int*)(*((int*)&d + 3)) + 1));
(*pRunB2)(); //调用vfptr[1](函数地址值)指向的函数
std::cout << " object d2 vfptr[2] func ptr is: " << (int*)*((int*)(*((int*)&d + 3)) + 2) << std::endl; //vfptr[2]的值
std::cout << " object d2 vfptr[2] func invoke res: ";
pFunc pRunD2 = (pFunc)(*((int*)(*((int*)&d + 3)) + 2));
(*pRunD2)(); //调用vfptr[2](函数地址值)指向的函数
std::cout << " object _b addr is: " << (int*)&d + 4 << std::endl; //_b 在对象第5个位置
std::cout << " object _b value is: " << *((int*)&d + 4) << std::endl;
std::cout << " object _d2 addr is: " << (int*)&d + 5 << std::endl; //_base2_var 在对象第6个位置
std::cout << " object _d2 value is: " << *((int*)&d + 5) << std::endl;
std::cout << "------ d2 obj end -----" << std::endl;
std::cout << " object _rhombus addr is: " << (int*)&d + 6 << std::endl; //_rhombus 在对象第7个位置
std::cout << " object _rhombus value is: " << *((int*)&d + 6) << std::endl;
std::cout << "---------end rhombus 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
29
30
31
32
33
34
35
---------begin rhombus common derive object inner memory layout test-------
object size is: 28
object addr is: 005AFB48
typeid(d) is: class Rhombus
------ d1 obj begin -----
object d1 vfptr addr is: 00DDEC18
object d1 vfptr[0] func ptr is: 00DD10E6
object d1 vfptr[0] func invoke res: Rhombus Run()
object d1 vfptr[1] func ptr is: 00DD13D4
object d1 vfptr[1] func invoke res: B RunB()
object d1 vfptr[2] func ptr is: 00DD1069
object d1 vfptr[2] func invoke res: D1 RunD1()
object d1 vfptr[3] func ptr is: 00DD125D
object d1 vfptr[3] func invoke res: Rhombus RunRhombus()
object _b addr is: 005AFB4C
object _b value is: 30
object _d1 addr is: 005AFB50
object _d1 value is: 30
------ d1 obj end -----
------ d2 obj begin -----
object d2 vfptr addr is: 00DDEC30
object d2 vfptr[0] func ptr is: 00DD1041
object d2 vfptr[0] func invoke res: Rhombus Run()
object d2 vfptr[1] func ptr is: 00DD13D4
object d2 vfptr[1] func invoke res: B RunB()
object d2 vfptr[2] func ptr is: 00DD13E8
object d2 vfptr[2] func invoke res: D2 RunD2()
object _b addr is: 005AFB58
object _b value is: 30
object _d2 addr is: 005AFB5C
object _d2 value is: 30
------ d2 obj end -----
object _rhombus addr is: 005AFB60
object _rhombus value is: 30
---------end rhombus common derive object inner memory layout test-------

3.内存布局示意图:
rhombus 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
40
41
42
43
44
45
46
47
48
49
50
51
52
53
class D1_Virtual : virtual public B
{
public:
D1_Virtual(int var = 20) : B(var), _d1_virtual(var) {};
~D1_Virtual() {}
public:
virtual void Run(void)
{
std::cout << "D1_Virtual Run()" << std::endl;
}
virtual void RunD1_Virtual(void)
{
std::cout << "D1_Virtual RunD1_Virtual()" << std::endl;
}
private:
int _d1_virtual;
};
class D2_Virtual : virtual public B
{
public:
D2_Virtual(int var = 20) : B(var), _d2_virtual(var) {};
~D2_Virtual() {}
public:
virtual void Run(void)
{
std::cout << "D2_Virtual Run()" << std::endl;
}
virtual void RunD2_Virtual(void)
{
std::cout << "D2_Virtual RunD2_Virtual()" << std::endl;
}
private:
int _d2_virtual;
};
class Rhombus_Virtual : public D1_Virtual, public D2_Virtual
{
public:
Rhombus_Virtual(int var = 30) : D1_Virtual(var), D2_Virtual(var), _rhombus_virtual(var) {};
~Rhombus_Virtual() {}
public:
virtual void Run(void)
{
std::cout << "Rhombus_Virtual Run()" << std::endl;
}
virtual void RunRhombus_Virtual(void)
{
std::cout << "Rhombus_Virtual RunRhombus_Virtual()" << std::endl;
}
private:
int _rhombus_virtual;
};

cl命令的内存布局

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
48
49
50
51
52
53
54
class Rhombus_Virtual size(80):
+---
0 | +--- (base class D1_Virtual)
0 | | {vfptr}
8 | | {vbptr}
16 | | _d1_virtual
| | <alignment member> (size=4)
| | <alignment member> (size=4)
| +---
24 | +--- (base class D2_Virtual)
24 | | {vfptr}
32 | | {vbptr}
40 | | _d2_virtual
| | <alignment member> (size=4)
| | <alignment member> (size=4)
| +---
48 | _rhombus_virtual
| <alignment member> (size=4)
| <alignment member> (size=4)
+---
60 | (vtordisp for vbase B)
+--- (virtual base B)
64 | {vfptr}
72 | _b
| <alignment member> (size=4)
+---
Rhombus_Virtual::$vftable@D1_Virtual@:
| &Rhombus_Virtual_meta
| 0
0 | &D1_Virtual::RunD1_Virtual
1 | &Rhombus_Virtual::RunRhombus_Virtual
Rhombus_Virtual::$vftable@D2_Virtual@:
| -24
0 | &D2_Virtual::RunD2_Virtual
Rhombus_Virtual::$vbtable@D1_Virtual@:
0 | -8
1 | 56 (Rhombus_Virtuald(D1_Virtual+8)B)
Rhombus_Virtual::$vbtable@D2_Virtual@:
0 | -8
1 | 32 (Rhombus_Virtuald(D2_Virtual+8)B)
Rhombus_Virtual::$vftable@B@:
| -64
0 | &(vtordisp) Rhombus_Virtual::Run
1 | &B::RunB
Rhombus_Virtual::Run this adjustor: 64
Rhombus_Virtual::RunRhombus_Virtual this adjustor: 0
vbi: class offset o.vbptr o.vbte fVtorDisp
B 64 8 4 1

可以看到虚拟菱形继承类的内存空间特点是基类Di的内存布局(按照继承的顺序依次布局基类)+子类的成员变量+vtordisp+共同虚基类B的内存布局,基类Di的内存布局包括基类Di的虚函数表和虚基表和基类Di的成员变量。子类的虚函数在继承而来的第一个继承的Di基类的虚函数表基础上进行新增子类操作(新的虚函数)。可以看到虚拟菱形继承内存中只存在一份公共基类B的结构,这样可以解决二义性问题

内存布局的代码验证

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
48
49
50
51
52
53
54
55
56
57
58
59
60
void RhombusVirtualDeriveTest(void)
{
Rhombus_Virtual d;
std::cout << "---------begin rhombus virtual 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指针转换为int指针
std::cout << " typeid(d) is: " << typeid(d).name() << std::endl; // d对象类型
std::cout << "------ d1 obj begin -----" << std::endl;
std::cout << " object d1 vfptr addr is: " << (int*)*(int*)&d << std::endl; // d1 vfptr地址 在对象第1个位置
std::cout << " object d1 vfptr[0] func ptr is: " << (int*)*(int*)(*(int*)&d) << std::endl; //vfptr[0]的值
std::cout << " object d1 vfptr[0] func invoke res: ";
pFunc pRunD1 = (pFunc)(*(int*)(*(int*)&d));
(*pRunD1)(); //调用vfptr[0](函数地址值)指向的函数
std::cout << " object d1 vfptr[1] func ptr is: " << (int*)*((int*)(*(int*)&d) + 1) << std::endl; //vfptr[1]的值
std::cout << " object d1 vfptr[1] func invoke res: ";
pFunc pRunRhombus_Virtual = (pFunc)(*((int*)(*(int*)&d) + 1));
(*pRunRhombus_Virtual)(); //调用vfptr[1](函数地址值)指向的函数
std::cout << " object d1 vbptr addr is: " << (int*)*((int*)&d + 1) << std::endl; // d1 vbptr 地址 在对象第2个位置
std::cout << " object d1 vbptr[0] value is: " << *((int*)*((int*)&d + 1)) << std::endl; // vbptr[0]的值: vbptr在对象中的偏移,负值
std::cout << " object d1 vbptr[1] value is: " << *((int*)*((int*)&d + 1) + 1) << std::endl; // vbptr[1]的值: 虚基类base在对象中的偏移,正值
std::cout << " object _d1 addr is: " << (int*)&d + 2 << std::endl; //_d1 在对象第3个位置
std::cout << " object _d1 value is: " << *((int*)&d + 2) << std::endl;
std::cout << "------ d1 obj end -----" << std::endl;
std::cout << "------ d2 obj begin -----" << std::endl;
std::cout << " object d2 vfptr addr is: " << (int*)*((int*)&d + 3) << std::endl; // d2 vfptr地址 在对象第4个位置
std::cout << " object d2 vfptr[0] func ptr is: " << (int*)*(int*)(*((int*)&d + 3)) << std::endl; //vfptr[0]的值
std::cout << " object d2 vfptr[0] func invoke res: ";
pFunc pRunD2 = (pFunc)(*(int*)(*((int*)&d + 3)));
(*pRunD2)(); //调用vfptr[0](函数地址值)指向的函数
std::cout << " object d2 vbptr addr is: " << (int*)*((int*)&d + 4) << std::endl; // d1 vbptr 地址 在对象第5个位置
std::cout << " object d2 vbptr[0] value is: " << *((int*)*((int*)&d + 4)) << std::endl; // vbptr[0]的值: vbptr在对象中的偏移,负值
std::cout << " object d2 vbptr[1] value is: " << *((int*)*((int*)&d + 4) + 1) << std::endl; // vbptr[1]的值: 虚基类base在对象中的偏移,正值
std::cout << " object _d2 addr is: " << (int*)&d + 5 << std::endl; //_d2 在对象第6个位置
std::cout << " object _d2 value is: " << *((int*)&d + 5) << std::endl;
std::cout << "------ d2 obj end -----" << std::endl;
std::cout << " object _rhombus_virtual addr is: " << (int*)&d + 6 << std::endl; //_rhombus_virtual 在对象第7个位置
std::cout << " object _rhombus_virtual value is: " << *((int*)&d + 6) << std::endl;
std::cout << " object vtordisp for vbase Base addr is: " << (int*)&d + 7 << std::endl; //vtordisp for virtual base 在对象第8个位置
std::cout << " object vtordisp for vbase Base value is: " << *((int*)&d + 7) << std::endl;
std::cout << " object b vfptr addr is: " << (int*)*((int*)&d + 8) << std::endl; // b vfptr地址 在对象第9个位置
std::cout << " object b vfptr[0] func ptr is: " << (int*)*(int*)(*((int*)&d + 8)) << std::endl; //vfptr[0]的值
std::cout << " object b vfptr[0] func invoke res: ";
pFunc pRun = (pFunc)(*(int*)(*((int*)&d + 8)));
(*pRun)(); //调用vfptr[0](函数地址值)指向的函数
std::cout << " object b vfptr[1] func ptr is: " << (int*)(*((int*)(*((int*)&d + 8)) + 1)) << std::endl; //vfptr[0]的值
std::cout << " object b vfptr[1] func invoke res: ";
pFunc pRunB = (pFunc)(*((int*)(*((int*)&d + 8)) + 1));
(*pRunB)(); //调用vfptr[0](函数地址值)指向的函数
std::cout << " object _b addr is: " << (int*)&d + 9 << std::endl; //_b 在对象第10个位置
std::cout << " object _b value is: " << *((int*)&d + 9) << std::endl;
std::cout << "---------end rhombus virtual 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
29
30
31
32
33
34
35
36
37
38
---------begin rhombus virtual derive object inner memory layout test-------
object size is: 40
object addr is: 012FFCF0
typeid(d) is: class Rhombus_Virtual
------ d1 obj begin -----
object d1 vfptr addr is: 00B51210
object d1 vfptr[0] func ptr is: 00B413E3
object d1 vfptr[0] func invoke res: D1_Virtual RunD1_Virtual()
object d1 vfptr[1] func ptr is: 00B413C5
object d1 vfptr[1] func invoke res: Rhombus_Virtual RunRhombus_Virtual()
object d1 vbptr addr is: 00B51238
object d1 vbptr[0] value is: -4
object d1 vbptr[1] value is: 28
object _d1 addr is: 012FFCF8
object _d1 value is: 30
------ d1 obj end -----
------ d2 obj begin -----
object d2 vfptr addr is: 00B51220
object d2 vfptr[0] func ptr is: 00B41195
object d2 vfptr[0] func invoke res: D2_Virtual RunD2_Virtual()
object d2 vbptr addr is: 00B51244
object d2 vbptr[0] value is: -4
object d2 vbptr[1] value is: 16
object _d2 addr is: 012FFD04
object _d2 value is: 30
------ d2 obj end -----
object _rhombus_virtual addr is: 012FFD08
object _rhombus_virtual value is: 30
object vtordisp for vbase Base addr is: 012FFD0C
object vtordisp for vbase Base value is: 0
object b vfptr addr is: 00B5122C
object b vfptr[0] func ptr is: 00B41546
object b vfptr[0] func invoke res: Rhombus_Virtual Run()
object b vfptr[1] func ptr is: 00B4141A
object b vfptr[1] func invoke res: B RunB()
object _b addr is: 012FFD14
object _b value is: 10
---------end rhombus virtual derive object inner memory layout test-------

3.内存布局示意图:
rhombus virtual derive

总结


本篇主要介绍了C++类在菱形继承体系下类对象内存特点。可以看出,对于普通菱形继承继承,最终的子类对象中会存在共同基类的多份拷贝,当子类调用共同基类中的相同函数时会存在二义性问题,为了解决这个问题,最好是在第二层继承体系中使用虚拟继承,这样在最终的子类的内存中会仅有一个共同基类的拷贝,主要是因为最终子类的多个虚基表都指向同一个虚基类,对于虚函数表依然遵守前几篇介绍的基本原则,由于菱形继承肯定是多重继承,所以最终子类有多张虚函数表,对于虚拟情况下的菱形继承还存在多张虚基表