日期 | 更新内容 |
---|---|
2020.12.17上午 | 之前虚继承部分有问题,重新进行了测试,更新了结构图 |
2020.12.17下午 | 添加了gcc测试 |
2020.12.17晚上 | 添加了clang测试 |
1 C++对象模型
C中表示结构化数据一般是使用struct,其中操作数据和具体的数据定义时分开的。C++中的对象是将数据和操作方式包装在一起的ADT(抽象数据类型)。在C++中数据成员分为static,nonstatic
,而成员函数分为三种:static,nonstatic,virtual
。
现在假定有类base
,base的声明如下:
class base
{
public:
base(int val = 0){}
virtual ~base(){}
static int count_object(){ return _count; };
int get_x(){ return _x; };
protected:
virtual ostream& print(ostream &) const;
protected:
int _x;
char _ch;
static int _count;
};
那么在不同的C++模型中具体的结构不尽相同。
下面的对象模型图,只是示意图,每一个单元并不表示具体的内存大小。
1.1 简单对象模型
简单对象模型就是对象中的内存单元存储一系列指针,每个指针指向对应的成员函数或者成员变量。当需要访问对象的成员时,需要进行多次寻址才能访问到,并且每个元素都会有一个对应的指针,势必有空间上的浪费。这种方式是通过牺牲空间和执行效率换取编译器设计上的简单性。
1.2 表格驱动对象模型
表格驱动模型将成员变量和成员函数分开两个表格存储,对象中只保存两个表格的指针。直观上看每个对象的大小基本是固定的,就是两个指针的大小,但是问题是在访问数据是需要进行多次间接寻址,相比简单对象模型成员变量只需一次寻址,而成员函数需要两次寻址。
1.3 C++实现对象模型
在实际的C++实现中,具体的方案是将nonstatic
的成员变量保存在类中,而static
的成员变量,所有成员函数都被存放在类外。同时虚函数的只是使用一个虚函数表,虚函数表中保存了当前对象中虚函数地址,在实现多态时会对相应的地址进行替换。并且类所关联的type_info
存放在虚函数表中的第一个单元中。而虚函数表的设置,重置等工作大部分完成于构造函数,拷贝构造函数,析构函数等中。
当前模型的有点事在空间和执行效率上都能够兼顾,缺陷时类相关的任何nonstatic
的成员变量的修改都需要重新编译程序。
2 C++和C之间的数据模型差异
2.1 结构化数据
C中的结构化数据模型struct
基本都是按照所声明的内存布局进行分配。而因为C++支持继承,也意味着C++中的结构化数据类型struct
和class
无法完全保证其数据存储方式能够完全按照用户预想的那样存储。
最直观的例子是,在C中通常在结构体结尾声明一个char buf[]
,即柔性数组来保证数据上的连续性,但是在C++中由于子类父类之间的内存布局破坏了这一简单有效的方式。
但是C++在某种程度上是兼容C的,因此可以通过C方式声明数据,然后通过组合的方式将C声明的数据使用C++中的class
包裹起来,这种基本等于给C代码添加了一个C++的壳,但是不会破坏C中的数据声明的空间布局。
class cpp_shell
{
public:
//C数据
C_data _c_data;
//操作函数
}
2.2 设计模型
程序模型:C++兼容C语言的程序模型,即面向过程程序设计。
抽象数据模型:将数据和具体的操作函数通过C++的class
进行绑定,并且重载一些数据常用的操作符,比如+,==,+=
等。比较常见的如:std::string
;
面向对象模型:面向对象模型基本就是通过一个抽象的基类进行公共接口的抽象,而其派生的子类中实现具体的方法,在运行期通过RTTI进行类型推断,实现多态。
3 C++对象模型测试
这里会测试visual studio cl,g++,clang三种编译器的测试结果,一切以visual studio cl的测试结果为准,如果后面测试的结构和visual studio相同则不会再重新画结构图,只贴出测试结果。
3.1 Visual Studio CL
内存模测试Visual Studio使用的cl编译器上的情况。
另外这里也会尝试使用cl.exe
查看内存模型和虚函数表,会使用到的命令如下:
cl /d1 reportSingleClassLayout[class_name] [filename]
其中class_name
为需要查看的类名,filename
为要查看的文件名。
另外下面的示意图中,只会显示在对象内存中的内容,诸如静态成员函数等不在对象中的内容不会再进行绘制。并且图中的一个单元格严格表示一个字节。
完整测试代码见grayondream/github
3.1.1 简单对象
class base
{
public:
base();
~base();
static void static_func();
void nonstatic_func();
public:
int _nonstatic_x;
static int _static_x;
char _nonstatic_ch;
};
上面的简单类型包含静态成员函数,非静态成员函数,静态成员变量和非静态成员变量。测试得到的不同。具体的测试结果如下:
对象base的实例b的大小为: 8
对象base的实例b的地址: 000000B0282FF618
对象base的实例b的成员变量_nonstatic_x的地址: base::_nonstatic_x 000000B0282FF618
对象base的实例b的成员变量_nonstatic_ch的地址: base::_nonstatic_ch 000000B0282FF61C
对象base的静态成员变量_static_x的地址: base::_static_x 00007FF6FE9CD000
对象base的成员函数static_func地址: base::static_func 00007FF6FE9C1127
对象base的成员函数nonstatic_func的地址: base::nonstatic_func 1
cl查看得到的内存布局如下:
class base size(8):
+---
0 | _nonstatic_x
4 | _nonstatic_ch
| <alignment member> (size=3)
+---
基本可以看到对象中占用对象大小的只有非静态变量和相关的内存对齐(当然虚函数表指针也会占用空间,但是当前类没有虚函数表)。另外从上面的输出能够看到静态函数和静态变量存放在相离不远的内存区域,并不在对象中。同时获取到的非静态成员函数地址并未成功打印,一个可靠的可能是如何获取成员函数地址?。
根据上面的结构,大概可以绘制出对象的模型如下:
这里已经看到静态类型和对象内存不在同一地区,因此下面的类不会再包含静态类型的数据。
3.1.2 包含虚函数的简单对象
当前测试使用的类如下,相比上面的base移除了多余的static
成员函数和成员变量,添加了一个虚函数。
class virtual_base
{
public:
virtual_base();
~virtual_base();
void nonstatic_func();
virtual void virtual_func1();
virtual void virtual_func2();
virtual void virtual_func3();
virtual void virtual_func4();
public:
int _nonstatic_x;
char _nonstatic_ch;
};
上面的对象只包含一个虚函数,测试结果如下:
对象virtual_base的实例b的大小为: 16
对象virtual_base的实例b的地址: 00000081EF6FF488
对象virtual_base的实例b的成员变量_nonstatic_x的地址: virtual_base::_nonstatic_x 00000081EF6FF490
对象virtual_base的实例b的成员变量_nonstatic_ch的地址: virtual_base::_nonstatic_ch 00000081EF6FF494
第0个虚函数执行结果: virtual_base::virtual_func1
对象virtual_base的第0个虚函数地址: 00007FF77CBD147E
第1个虚函数执行结果: virtual_base::virtual_func2
对象virtual_base的第1个虚函数地址: 00007FF77CBD1479
第2个虚函数执行结果: virtual_base::virtual_func3
对象virtual_base的第2个虚函数地址: 00007FF77CBD1474
第3个虚函数执行结果: virtual_base::virtual_func4
对象virtual_base的第3个虚函数地址: 00007FF77CBD1483
可以看到虚函数表的地址存放在对象的开头,之后紧跟的是其他对象元素。因此对象大的小为sizeof(ptr) + sizeof(char) + sizeof(int) + 内存对齐
另外在对虚函数进行解析能够完全解析到相关的虚函数并执行得到执行结果。另外,RTTI相关的type_info
在表项的-1位置,通过调试器能够看到相关结构,但是没能成功解析出具体的参数值。
vptr[-1] 0x00007ff7241acf10 {cpp_memory_model.exe!const virtual_base::`RTTI Complete Object Locator'} void *
从cl的输出能够看到基本符合测试结果。
class virtual_base size(16):
+---
0 | {vfptr}
8 | _nonstatic_x
12 | _nonstatic_ch
| <alignment member> (size=3)
+---
virtual_base::$vftable@:
| &virtual_base_meta
| 0
0 | &virtual_base::virtual_func1
1 | &virtual_base::virtual_func2
2 | &virtual_base::virtual_func3
3 | &virtual_base::virtual_func4
从上面的测试结果得到的内存模型如下:
3.1.3 单继承内存模型
简单单继承模型继承自base
,并且只为其添加了一个简单的数据类型。
class inherit : public base
{
public:
char _inherit_ch;
};
从下面的测试可以看出基类存放在继承类的开头位置,之后紧跟的便是子类独有的成员变量。
对象inherit的实例b的大小为: 12
对象inherit的实例b的地址: 00000083A26FFA18
对象inherit的实例b的成员变量base::_nonstatic_x的地址: inherit::base::_nonstatic_x 00000083A26FFA18
对象inherit的实例b的成员变量base::_nonstatic_ch的地址: inherit::base::_nonstatic_ch 00000083A26FFA1C
对象inherit的实例b的成员变量_inherit_ch的地址: inherit::_inherit_ch 00000083A26FFA20
从下面基本可以看出完全符合测试的结果。
class inherit size(12):
+---
0 | +--- (base class base)
0 | | _nonstatic_x
4 | | _nonstatic_ch
| | <alignment member> (size=3)
| +---
8 | _inherit_ch
| <alignment member> (size=3)
+---
根据上面的结构得到的内存结构如下:
3.1.4 包含虚函数的单继承对象
单继承体系中,下面的类只重写了基类中的一个虚函数,并且添加了一个自身的虚函数。
class virtual_inherit : public virtual_base
{
public:
char _virtual_inherit_ch;
public:
virtual void virtual_func1();
};
测试结果如下:
对象virtual_inherit的实例b的大小为: 24
对象virtual_inherit的实例b的地址: 00000046DBFBF6E8
对象virtual_inherit的实例b的成员变量virtual_base::_nonstatic_x的地址: virtual_inherit::virtual_base::_nonstatic_x 00000046DBFBF6F0
对象virtual_inherit的实例b的成员变量virtual_base::_nonstatic_ch的地址: virtual_inherit::virtual_base::_nonstatic_ch 00000046DBFBF6F4
对象virtual_inherit的实例b的成员变量_virtual_inherit_ch的地址: virtual_inherit::_virtual_inherit_ch 00000046DBFBF6F8
第0个虚函数执行结果: virtual_inherit::virtual_func1
对象virtual_base的第0个虚函数地址: 00007FF6E2671136
第1个虚函数执行结果: virtual_base::virtual_func2
对象virtual_base的第1个虚函数地址: 00007FF6E267104B
第2个虚函数执行结果: virtual_base::virtual_func3
对象virtual_base的第2个虚函数地址: 00007FF6E2671005
第3个虚函数执行结果: virtual_base::virtual_func4
对象virtual_base的第3个虚函数地址: 00007FF6E26713F2
第4个虚函数执行结果: virtual_inherit::virtual_inherit_func
对象virtual_base的第4个虚函数地址: 00007FF6E2671497
根据上面的测试结果和下面的结构能够看到子类重写的虚函数在类中的虚函数表中被替换,并且当前类新增的虚函数被添加到虚函数表的末尾。
class virtual_inherit size(24):
+---
0 | +--- (base class virtual_base)
0 | | {vfptr}
8 | | _nonstatic_x
12 | | _nonstatic_ch
| | <alignment member> (size=3)
| +---
16 | _virtual_inherit_ch
| <alignment member> (size=7)
+---
virtual_inherit::$vftable@:
| &virtual_inherit_meta
| 0
0 | &virtual_inherit::virtual_func1
1 | &virtual_base::virtual_func2
2 | &virtual_base::virtual_func3
3 | &virtual_base::virtual_func4
4 | &virtual_inherit::virtual_inherit_func
根据上面的测试得到的内存模型如下:
3.1.5 多继承体系
继承体系非常简单只包含两个简单的基类。
class base_left
{
public:
int _base_left_int;
char _base_left_ch;
};
class base_right
{
public:
int _base_right_int;
char _base_right_ch;
};
class minherit: public base_left, public base_right
{
public:
char _minherit_ch;
};
从下面可以看出base_left
在继承类开头,之后紧跟的是base_right
,最后才是基类本身的数据。
对象minherit的实例b的大小为: 20
对象minherit的实例b的地址: 000000D03AEFF128
对象minherit的实例b的成员变量base_left::_base_left_int的地址: minherit::base_left::_base_left_int 000000D03AEFF128
对象minherit的实例b的成员变量base_left::_base_left_ch的地址: minherit::base_left::_base_left_ch 000000D03AEFF12C
对象minherit的实例b的成员变量base_right::_base_right_int的地址: minherit::base_right::_base_right_int 000000D03AEFF130
对象minherit的实例b的成员变量base_right::_base_right_ch的地址: minherit::base_right::_base_right_ch 000000D03AEFF134
对象minherit的实例b的成员变量_minherit_ch的地址: minherit::_minherit_ch 000000D03AEFF138
class minherit size(20):
+---
0 | +--- (base class base_left)
0 | | _base_left_int
4 | | _base_left_ch
| | <alignment member> (size=3)
| +---
8 | +--- (base class base_right)
8 | | _base_right_int
12 | | _base_right_ch
| | <alignment member> (size=3)
| +---
16 | _minherit_ch
| <alignment member> (size=3)
+---
推断出的内存模型如下:
如果将继承顺序改为
class minherit: public base_right, public base_left
{
public:
char _minherit_ch;
};
内存中base_left
和base_right
换了个顺序
对象minherit的实例b的大小为: 20
对象minherit的实例b的地址: 000000E48634F8F8
对象minherit的实例b的成员变量base_left::_base_left_int的地址: minherit::base_left::_base_left_int 000000E48634F900
对象minherit的实例b的成员变量base_left::_base_left_ch的地址: minherit::base_left::_base_left_ch 000000E48634F904
对象minherit的实例b的成员变量base_right::_base_right_int的地址: minherit::base_right::_base_right_int 000000E48634F8F8
对象minherit的实例b的成员变量base_right::_base_right_ch的地址: minherit::base_right::_base_right_ch 000000E48634F8FC
对象minherit的实例b的成员变量_minherit_ch的地址: minherit::_minherit_ch 000000E48634F908
class minherit size(20):
+---
0 | +--- (base class base_right)
0 | | _base_right_int
4 | | _base_right_ch
| | <alignment member> (size=3)
| +---
8 | +--- (base class base_left)
8 | | _base_left_int
12 | | _base_left_ch
| | <alignment member> (size=3)
| +---
16 | _minherit_ch
| <alignment member> (size=3)
+---
3.1.6 包含虚函数的多继承体系
class vbase_left
{
public:
int _base_left_int;
char _base_left_ch;
public:
virtual void vbase_left_virtual_function1();
virtual void vbase_left_virtual_function2();
};
class vbase_right
{
public:
int _base_right_int;
char _base_right_ch;
public:
virtual void vbase_right_virtual_function1();
virtual void vbase_right_virtual_function2();
};
class vminherit : public vbase_left, public vbase_right
{
public:
char _minherit_ch;
public:
virtual void vminherit_virtual_function();
virtual void vbase_left_virtual_function1();
virtual void vbase_right_virtual_function1();
};
从下面的结构能够看到继承类中包含两个虚函数指针,分别存储在基类的开头,并且子类自身的虚函数被添加到继承顺序中第一个虚函数表中。
class vminherit size(40):
+---
0 | +--- (base class vbase_left)
0 | | {vfptr}
8 | | _base_left_int
12 | | _base_left_ch
| | <alignment member> (size=3)
| +---
16 | +--- (base class vbase_right)
16 | | {vfptr}
24 | | _base_right_int
28 | | _base_right_ch
| | <alignment member> (size=3)
| +---
32 | _minherit_ch
| <alignment member> (size=7)
+---
vminherit::$vftable@vbase_left@:
| &vminherit_meta
| 0
0 | &vminherit::vbase_left_virtual_function1
1 | &vbase_left::vbase_left_virtual_function2
2 | &vminherit::vminherit_virtual_function
vminherit::$vftable@vbase_right@:
| -16
0 | &vminherit::vbase_right_virtual_function1
1 | &vbase_right::vbase_right_virtual_function2
根据上面的结构推断到下面的结果如下:
对象vminherit的实例b的大小为: 40
对象vminherit的实例b的地址: 000000474F6FF388
对象vminherit的实例b的成员变量vbase_left::_base_left_int的地址: vminherit::vbase_left::_base_left_int 000000474F6FF390
对象vminherit的实例b的成员变量vbase_left::_base_left_ch的地址: vminherit::vbase_left::_base_left_ch 000000474F6FF394
对象vminherit的实例b的成员变量vbase_right::_base_right_int的地址: vminherit::vbase_right::_base_right_int 000000474F6FF3A0
对象vminherit的实例b的成员变量vbase_right::_base_right_ch的地址: vminherit::vbase_right::_base_right_ch 000000474F6FF3A4
对象vminherit的实例b的成员变量_minherit_ch的地址: vminherit::_minherit_ch 000000474F6FF3A8
第一个虚函数表:
第0个虚函数执行结果: vminherit::vbase_left_virtual_function1
对象vminherit的第0个虚函数地址: 00007FF7FCC514CE
第1个虚函数执行结果: vbase_left::vbase_left_virtual_function2
对象vminherit的第1个虚函数地址: 00007FF7FCC5135C
第2个虚函数执行结果: vminherit::vminherit_virtual_function
对象vminherit的第2个虚函数地址: 00007FF7FCC51429
第二个虚函数表:
第0个虚函数执行结果: vminherit::vbase_right_virtual_function1
对象vminherit的第0个虚函数地址: 00007FF7FCC514D3
第1个虚函数执行结果: vbase_right::vbase_right_virtual_function2
对象vminherit的第1个虚函数地址: 00007FF7FCC51280
推断到的结构模型如下:
3.1.7 不包含虚函数的菱形继承
菱形继承采用的结构如下:
class grand
{
public:
int _grand_x;
char _grand_ch;
};
class father : public grand
{
public:
char _father_ch;
};
class mother : public grand
{
public:
char _mother_ch;
};
class child : public mother, public father
{
public:
char _child_ch;
};
3.1.7.1 不使用虚继承
class child size(28):
+---
0 | +--- (base class mother)
0 | | +--- (base class grand)
0 | | | _grand_x
4 | | | _grand_ch
| | | <alignment member> (size=3)
| | +---
8 | | _mother_ch
| | <alignment member> (size=3)
| +---
12 | +--- (base class father)
12 | | +--- (base class grand)
12 | | | _grand_x
16 | | | _grand_ch
| | | <alignment member> (size=3)
| | +---
20 | | _father_ch
| | <alignment member> (size=3)
| +---
24 | _child_ch
| <alignment member> (size=3)
+---
从上面可以看到每个基类都会保存一份自身基类的副本。
对象child的实例b的大小为: 28
对象child的实例b的地址: 000000FA898FF7D8
对象child的实例b的成员变量father::grand::_grand_x的地址: child::father::grand::_grand_x 000000FA898FF7E4
对象child的实例b的成员变量father::grand::_grand_ch的地址: child::father::grand::_grand_ch 000000FA898FF7E8
对象child的实例b的成员变量father::_father_ch的地址: child::father::_father_ch 000000FA898FF7EC
对象child的实例b的成员变量mother::grand::_grand_x的地址: child::mother::grand::_grand_x 000000FA898FF7D8
对象child的实例b的成员变量mother::grand::_grand_ch的地址: child::mother::grand::_grand_ch 000000FA898FF7DC
对象child的实例b的成员变量mother::_mother_ch的地址: child::mother::_mother_ch 000000FA898FF7E0
对象child的实例b的成员变量_child_ch的地址: child::_child_ch 000000FA898FF7F0
推导出的内存模型如下:
3.1.7.2 使用虚继承
#pragma once
class grand
{
public:
int _grand_x;
char _grand_ch;
};
class father : virtual public grand
{
public:
char _father_ch;
};
class mother : virtual public grand
{
public:
char _mother_ch;
};
class child : public mother, public father
{
public:
char _child_ch;
};
使用虚继承之后结构变为如下:
class child size(48):
+---
0 | +--- (base class mother)
0 | | {vbptr}
8 | | _mother_ch
| | <alignment member> (size=7)
| +---
16 | +--- (base class father)
16 | | {vbptr}
24 | | _father_ch
| | <alignment member> (size=7)
| +---
32 | _child_ch
| <alignment member> (size=7)
+---
+--- (virtual base grand)
40 | _grand_x
44 | _grand_ch
| <alignment member> (size=3)
+---
child::$vbtable@mother@:
0 | 0
1 | 40 (childd(mother+0)grand)
child::$vbtable@father@:
0 | 0
1 | 24 (childd(father+0)grand)
vbi: class offset o.vbptr o.vbte fVtorDisp
grand 40 0 4 0
测试结果如下:
对象child的实例b的大小为: 48
对象child的实例b的地址: 0000000D68B2F618
对象child的实例b的成员变量father::_grand_x的地址: child::father::_grand_x 0000000D68B2F640
对象child的实例b的成员变量father::_grand_ch的地址: child::father::_grand_ch 0000000D68B2F644
对象child的实例b的成员变量father::_father_ch的地址: child::father::_father_ch 0000000D68B2F630
对象child的实例b的成员变量mother::_grand_x的地址: child::mother::_grand_x 0000000D68B2F640
对象child的实例b的成员变量mother::_grand_ch的地址: child::mother::_grand_ch 0000000D68B2F644
对象child的实例b的成员变量mother::_mother_ch的地址: child::mother::_mother_ch 0000000D68B2F620
对象child的实例b的成员变量_child_ch的地址: child::_child_ch 0000000D68B2F638
从上面的结构能够看到内存中只有一份共享的grand
,但是问题是怎么空间还比没有虚继承的时候大了,这是因为child
的两个父类mother
和father
为了知道它们共享的基类grand
的位置在内存中添加了一个指针vbptr
,用来指向基类偏移量的表格,因为可能存在多个共享的基类。也就是说visual studio的虚继承实现是通过保存偏移到一个表格中,然后为每个对象添加指针指向对应的偏移,如果编译器需要知道每个父类对象所对应的基类的地址,只需要addr+offset
即可。
3.1.8 包含虚函数的菱形继承
class vgrand
{
public:
virtual void vgrand_virtual_func1();
virtual void vgrand_virtual_func2();
virtual void vgrand_virtual_func3();
virtual void vgrand_virtual_func4();
virtual void vgrand_virtual_func5();
public:
int _vgrand_x;
char _vgrand_ch;
};
class vfather : public vgrand
{
public:
virtual void vfather_virtual_func1();
virtual void vfather_virtual_func2();
virtual void vgrand_virtual_func1();
virtual void vgrand_virtual_func3();
public:
char _vfather_ch;
};
class vmother : public vgrand
{
public:
virtual void vmother_virtual_func1();
virtual void vmother_virtual_func2();
virtual void vgrand_virtual_func2();
virtual void vgrand_virtual_func3();
public:
char _vmother_ch;
};
class vchild : public vmother, public vfather
{
public:
void vchild_virtual_func();
virtual void vgrand_virtual_func4();
virtual void vfather_virtual_func1();
virtual void vmother_virtual_func1();
public:
char _vchild_ch;
};
3.1.8.1 不使用虚继承
class vchild size(56):
+---
0 | +--- (base class vmother)
0 | | +--- (base class vgrand)
0 | | | {vfptr}
8 | | | _vgrand_x
12 | | | _vgrand_ch
| | | <alignment member> (size=3)
| | +---
16 | | _vmother_ch
| | <alignment member> (size=7)
| +---
24 | +--- (base class vfather)
24 | | +--- (base class vgrand)
24 | | | {vfptr}
32 | | | _vgrand_x
36 | | | _vgrand_ch
| | | <alignment member> (size=3)
| | +---
40 | | _vfather_ch
| | <alignment member> (size=7)
| +---
48 | _vchild_ch
| <alignment member> (size=7)
+---
vchild::$vftable@vmother@:
| &vchild_meta
| 0
0 | &vgrand::vgrand_virtual_func1
1 | &vmother::vgrand_virtual_func2
2 | &vmother::vgrand_virtual_func3
3 | &vchild::vgrand_virtual_func4
4 | &vgrand::vgrand_virtual_func5
5 | &vchild::vmother_virtual_func1
6 | &vmother::vmother_virtual_func2
vchild::$vftable@vfather@:
| -24
0 | &vfather::vgrand_virtual_func1
1 | &vgrand::vgrand_virtual_func2
2 | &vfather::vgrand_virtual_func3
3 | &thunk: this-=24; goto vchild::vgrand_virtual_func4
4 | &vgrand::vgrand_virtual_func5
5 | &vchild::vfather_virtual_func1
6 | &vfather::vfather_virtual_func2
对象vchild的实例b的大小为: 56
对象vchild的实例b的地址: 0000008C94DFF008
对象vchild的实例b的成员变量vfather::vgrand::_vgrand_x的地址: vchild::vfather::vgrand::_vgrand_x 0000008C94DFF028
对象vchild的实例b的成员变量vfather::vgrand::_vgrand_ch的地址: vchild::vfather::vgrand::_vgrand_ch 0000008C94DFF02C
对象vchild的实例b的成员变量vfather::_vfather_ch的地址: vchild::vfather::_vfather_ch 0000008C94DFF030
对象vchild的实例b的成员变量vmother::vgrand::_vgrand_x的地址: vchild::vmother::vgrand::_vgrand_x 0000008C94DFF010
对象vchild的实例b的成员变量vmother::vgrand::_vgrand_ch的地址: vchild::vmother::vgrand::_vgrand_ch 0000008C94DFF014
对象vchild的实例b的成员变量vmother::_vmother_ch的地址: vchild::vmother::_vmother_ch 0000008C94DFF018
对象vchild的实例b的成员变量_vchild_ch的地址: vchild::_vchild_ch 0000008C94DFF038
第一个虚函数表:
第0个虚函数执行结果: vgrand::vgrand_virtual_func1
对象vchild的第0个虚函数地址: 00007FF7E83F114F
第1个虚函数执行结果: vmother::vgrand_virtual_func2
对象vchild的第1个虚函数地址: 00007FF7E83F12CB
第2个虚函数执行结果: vmother::vgrand_virtual_func3
对象vchild的第2个虚函数地址: 00007FF7E83F1267
第3个虚函数执行结果: vchild::vgrand_virtual_func4
对象vchild的第3个虚函数地址: 00007FF7E83F12DF
第4个虚函数执行结果: vgrand::vgrand_virtual_func5
对象vchild的第4个虚函数地址: 00007FF7E83F1384
第5个虚函数执行结果: vchild::vmother_virtual_func1
对象vchild的第5个虚函数地址: 00007FF7E83F11CC
第6个虚函数执行结果: vmother::vmother_virtual_func2
对象vchild的第6个虚函数地址: 00007FF7E83F112C
第二个虚函数表:
第0个虚函数执行结果: vfather::vgrand_virtual_func1
对象vchild的第0个虚函数地址: 00007FF7E83F101E
第1个虚函数执行结果: vgrand::vgrand_virtual_func2
对象vchild的第1个虚函数地址: 00007FF7E83F100F
第2个虚函数执行结果: vfather::vgrand_virtual_func3
对象vchild的第2个虚函数地址: 00007FF7E83F1118
第3个虚函数执行结果: vchild::vgrand_virtual_func4
对象vchild的第3个虚函数地址: 00007FF7E83F1497
第4个虚函数执行结果: vgrand::vgrand_virtual_func5
对象vchild的第4个虚函数地址: 00007FF7E83F1384
第5个虚函数执行结果: vchild::vfather_virtual_func1
对象vchild的第5个虚函数地址: 00007FF7E83F1032
第6个虚函数执行结果: vfather::vfather_virtual_func2
对象vchild的第6个虚函数地址: 00007FF7E83F10B4
推断得到的对象模型如下:
3.1.8.2 使用虚继承
#pragma once
class vgrand
{
public:
virtual void vgrand_virtual_func1();
virtual void vgrand_virtual_func2();
virtual void vgrand_virtual_func3();
virtual void vgrand_virtual_func4();
virtual void vgrand_virtual_func5();
public:
int _vgrand_x;
char _vgrand_ch;
};
class vfather : virtual public vgrand
{
public:
virtual void vfather_virtual_func1();
virtual void vfather_virtual_func2();
virtual void vgrand_virtual_func1();
virtual void vgrand_virtual_func3();
public:
char _vfather_ch;
};
class vmother : virtual public vgrand
{
public:
virtual void vmother_virtual_func1();
virtual void vmother_virtual_func2();
virtual void vgrand_virtual_func2();
virtual void vgrand_virtual_func3();
public:
char _vmother_ch;
};
class vchild : public vmother, public vfather
{
public:
void vchild_virtual_func();
virtual void vgrand_virtual_func4();
virtual void vfather_virtual_func1();
virtual void vmother_virtual_func1();
virtual void vgrand_virtual_func3(); //如果不对当前函数进行重写,则编译器并无知道运行时应该调用vmother::vgrand_virtual_func3还是vfather::vgrand_virtaul_func3
public:
char _vchild_ch;
};
class vchild size(72):
+---
0 | +--- (base class vmother)
0 | | {vfptr}
8 | | {vbptr}
16 | | _vmother_ch
| | <alignment member> (size=7)
| +---
24 | +--- (base class vfather)
24 | | {vfptr}
32 | | {vbptr}
40 | | _vfather_ch
| | <alignment member> (size=7)
| +---
48 | _vchild_ch
| <alignment member> (size=7)
+---
+--- (virtual base vgrand)
56 | {vfptr}
64 | _vgrand_x
68 | _vgrand_ch
| <alignment member> (size=3)
+---
vchild::$vftable@vmother@:
| &vchild_meta
| 0
0 | &vchild::vmother_virtual_func1
1 | &vmother::vmother_virtual_func2
vchild::$vftable@vfather@:
| -24
0 | &vchild::vfather_virtual_func1
1 | &vfather::vfather_virtual_func2
vchild::$vbtable@vmother@:
0 | -8
1 | 48 (vchildd(vmother+8)vgrand)
vchild::$vbtable@vfather@:
0 | -8
1 | 24 (vchildd(vfather+8)vgrand)
vchild::$vftable@vgrand@:
| -56
0 | &thunk: this-=8; goto vfather::vgrand_virtual_func1
1 | &thunk: this-=32; goto vmother::vgrand_virtual_func2
2 | &vchild::vgrand_virtual_func3
3 | &vchild::vgrand_virtual_func4
4 | &vgrand::vgrand_virtual_func5
vchild::vgrand_virtual_func4 this adjustor: 56
vchild::vfather_virtual_func1 this adjustor: 24
vchild::vmother_virtual_func1 this adjustor: 0
vchild::vgrand_virtual_func3 this adjustor: 56
vbi: class offset o.vbptr o.vbte fVtorDisp
vgrand 56 8 4 0
对象vchild的实例b的大小为: 72
对象vchild的实例b的地址: 00000037DC9CF660
对象vchild的实例b的成员变量vfather::_vgrand_x的地址: vchild::vfather::_vgrand_x 00000037DC9CF6A0
对象vchild的实例b的成员变量vfather::_vgrand_ch的地址: vchild::vfather::_vgrand_ch 00000037DC9CF6A4
对象vchild的实例b的成员变量vfather::_vfather_ch的地址: vchild::vfather::_vfather_ch 00000037DC9CF688
对象vchild的实例b的成员变量vmother::_vgrand_x的地址: vchild::vmother::_vgrand_x 00000037DC9CF6A0
对象vchild的实例b的成员变量vmother::_vgrand_ch的地址: vchild::vmother::_vgrand_ch 00000037DC9CF6A4
对象vchild的实例b的成员变量vmother::_vmother_ch的地址: vchild::vmother::_vmother_ch 00000037DC9CF670
对象vchild的实例b的成员变量_vchild_ch的地址: vchild::_vchild_ch 00000037DC9CF690
第一个虚函数表:
第0个虚函数执行结果: vchild::vmother_virtual_func1
对象vchild的第0个虚函数地址: 00007FF7C359115E
第1个虚函数执行结果: vmother::vmother_virtual_func2
对象vchild的第1个虚函数地址: 00007FF7C35910EB
第二个虚函数表:
第0个虚函数执行结果: vchild::vfather_virtual_func1
对象vchild的第0个虚函数地址: 00007FF7C3591028
第1个虚函数执行结果: vfather::vfather_virtual_func2
对象vchild的第1个虚函数地址: 00007FF7C3591091
第三个虚函数表:
第0个虚函数执行结果: vfather::vgrand_virtual_func1
对象vchild的第0个虚函数地址: 00007FF7C3591087
第1个虚函数执行结果: vmother::vgrand_virtual_func2
对象vchild的第1个虚函数地址: 00007FF7C359111D
第2个虚函数执行结果: vchild::vgrand_virtual_func3
对象vchild的第2个虚函数地址: 00007FF7C35912B7
第3个虚函数执行结果: vchild::vgrand_virtual_func4
对象vchild的第3个虚函数地址: 00007FF7C3591212
第4个虚函数执行结果: vgrand::vgrand_virtual_func5
对象vchild的第4个虚函数地址: 00007FF7C3591280
推断到的内存分布如下:
3.2 G++
这里测试的编译器信息:gcc version 7.5.0 (Ubuntu 7.5.0-6ubuntu2)
,下面会使用g++ -fdump-class-hierarchy [filename]
查看类的结构。
测试代码采用上面同样的代码。
3.2.1 简单对象
Class base
size=8 align=4
base size=5 base align=4
base (0x0x7f3b22817960) 0
对象base的实例b的大小为: 8
对象base的实例b的地址: 0x7fffd1e71f70
对象base的实例b的成员变量_nonstatic_x的地址: base::_nonstatic_x 0x7fffd1e71f70
对象base的实例b的成员变量_nonstatic_ch的地址: base::_nonstatic_ch 0x7fffd1e71f74
对象base的静态成员变量_static_x的地址: base::_static_x 0x7feec64a7010
3.2.2 包含虚函数的简单对象
Vtable for virtual_base
virtual_base::_ZTV12virtual_base: 6 entries
0 (int (*)(...))0
8 (int (*)(...))(& _ZTI12virtual_base)
16 (int (*)(...))virtual_base::virtual_func1
24 (int (*)(...))virtual_base::virtual_func2
32 (int (*)(...))virtual_base::virtual_func3
40 (int (*)(...))virtual_base::virtual_func4
Class virtual_base
size=16 align=8
base size=13 base align=8
virtual_base (0x0x7fb0b3ea3900) 0
vptr=((& virtual_base::_ZTV12virtual_base) + 16)
对象virtual_base的实例b的大小为: 16
对象virtual_base的实例b的地址: 0x7fffcd756d50
对象virtual_base的实例b的成员变量_nonstatic_x的地址: virtual_base::_nonstatic_x 0x7fffcd756d58
对象virtual_base的实例b的成员变量_nonstatic_ch的地址: virtual_base::_nonstatic_ch 0x7fffcd756d5c
对象virtual_base的第1个虚函数表地址: virtual_base::vptr 0x7fffcd756d50
第0个虚函数执行结果: virtual_base::virtual_func1
对象virtual_base的第0个虚函数地址: 0x7f8adc7ce6f2
第1个虚函数执行结果: virtual_base::virtual_func2
对象virtual_base的第1个虚函数地址: 0x7f8adc7ce72a
第2个虚函数执行结果: virtual_base::virtual_func3
对象virtual_base的第2个虚函数地址: 0x7f8adc7ce762
第3个虚函数执行结果: virtual_base::virtual_func4
对象virtual_base的第3个虚函数地址: 0x7f8adc7ce79a
3.2.3 简单单继承
Class inherit
size=8 align=4
base size=6 base align=4
inherit (0x0x7f420c63d1a0) 0
base (0x0x7f420c7a79c0) 0
对象inherit的实例b的大小为: 8
对象inherit的实例b的地址: 0x7fffd71b95a0
对象inherit的实例b的成员变量base::_nonstatic_x的地址: inherit::base::_nonstatic_x 0x7fffd71b95a0
对象inherit的实例b的成员变量base::_nonstatic_ch的地址: inherit::base::_nonstatic_ch 0x7fffd71b95a4
对象inherit的实例b的成员变量_inherit_ch的地址: inherit::_inherit_ch 0x7fffd71b95a5
这里的结构和cl不同,cl并不会破坏基类的整体性,而gcc做了内存紧簇。
3.2.4 包含虚函数的简单单继承
Vtable for virtual_inherit
virtual_inherit::_ZTV15virtual_inherit: 7 entries
0 (int (*)(...))0
8 (int (*)(...))(& _ZTI15virtual_inherit)
16 (int (*)(...))virtual_inherit::virtual_func1
24 (int (*)(...))virtual_base::virtual_func2
32 (int (*)(...))virtual_base::virtual_func3
40 (int (*)(...))virtual_base::virtual_func4
48 (int (*)(...))virtual_inherit::virtual_inherit_func
Class virtual_inherit
size=16 align=8
base size=14 base align=8
virtual_inherit (0x0x7f8cad7b0138) 0
vptr=((& virtual_inherit::_ZTV15virtual_inherit) + 16)
virtual_base (0x0x7f8cad793960) 0
primary-for virtual_inherit (0x0x7f8cad7b0138)
对象virtual_inherit的实例b的大小为: 16
对象virtual_inherit的实例b的地址: 0x7fffdffcdb40
对象virtual_inherit的实例b的成员变量virtual_base::_nonstatic_x的地址: virtual_inherit::virtual_base::_nonstatic_x 0x7fffdffcdb48
对象virtual_inherit的实例b的成员变量virtual_base::_nonstatic_ch的地址: virtual_inherit::virtual_base::_nonstatic_ch 0x7fffdffcdb4c
对象virtual_inherit的实例b的成员变量_virtual_inherit_ch的地址: virtual_inherit::_virtual_inherit_ch 0x7fffdffcdb4d
对象virtual_inherit的第1个虚函数表地址: virtual_inherit::vptr 0x7fffdffcdb40
第0个虚函数执行结果: virtual_inherit::virtual_func1
对象virtual_base的第0个虚函数地址: 0x7f94adef9830
第1个虚函数执行结果: virtual_base::virtual_func2
对象virtual_base的第1个虚函数地址: 0x7f94adef972a
第2个虚函数执行结果: virtual_base::virtual_func3
对象virtual_base的第2个虚函数地址: 0x7f94adef9762
第3个虚函数执行结果: virtual_base::virtual_func4
对象virtual_base的第3个虚函数地址: 0x7f94adef979a
第4个虚函数执行结果: virtual_inherit::virtual_inherit_func
对象virtual_base的第4个虚函数地址: 0x7f94adef9868
其内存结构推断如下:
3.2.5 简单多继承
Class minherit
size=20 align=4
base size=17 base align=4
minherit (0x0x7fda3561e2a0) 0
base_right (0x0x7fda35777a20) 0
base_left (0x0x7fda35777a80) 8
对象minherit的实例b的大小为: 20
对象minherit的实例b的地址: 0x7ffff40425c0
对象minherit的实例b的成员变量base_left::_base_left_int的地址: minherit::base_left::_base_left_int 0x7ffff40425c8
对象minherit的实例b的成员变量base_left::_base_left_ch的地址: minherit::base_left::_base_left_ch 0x7ffff40425cc
对象minherit的实例b的成员变量base_right::_base_right_int的地址: minherit::base_right::_base_right_int 0x7ffff40425c0
对象minherit的实例b的成员变量base_right::_base_right_ch的地址: minherit::base_right::_base_right_ch 0x7ffff40425c4
对象minherit的实例b的成员变量_minherit_ch的地址: minherit::_minherit_ch 0x7ffff40425d0
3.2.6 包含虚函数的多继承
Vtable for vminherit
vminherit::_ZTV9vminherit: 10 entries
0 (int (*)(...))0
8 (int (*)(...))(& _ZTI9vminherit)
16 (int (*)(...))vminherit::vbase_left_virtual_function1
24 (int (*)(...))vbase_left::vbase_left_virtual_function2
32 (int (*)(...))vminherit::vminherit_virtual_function
40 (int (*)(...))vminherit::vbase_right_virtual_function1
48 (int (*)(...))-16
56 (int (*)(...))(& _ZTI9vminherit)
64 (int (*)(...))vminherit::_ZThn16_N9vminherit29vbase_right_virtual_function1Ev
72 (int (*)(...))vbase_right::vbase_right_virtual_function2
Class vminherit
size=32 align=8
base size=30 base align=8
vminherit (0x0x7f37ec85e380) 0
vptr=((& vminherit::_ZTV9vminherit) + 16)
vbase_left (0x0x7f37ec4239c0) 0
primary-for vminherit (0x0x7f37ec85e380)
vbase_right (0x0x7f37ec423a20) 16
vptr=((& vminherit::_ZTV9vminherit) + 64)
对象vminherit的实例b的大小为: 32
对象vminherit的实例b的地址: 0x7fffcded6160
对象vminherit的实例b的成员变量vbase_left::_base_left_int的地址: vminherit::vbase_left::_base_left_int 0x7fffcded6168
对象vminherit的实例b的成员变量vbase_left::_base_left_ch的地址: vminherit::vbase_left::_base_left_ch 0x7fffcded616c
对象vminherit的实例b的成员变量vbase_right::_base_right_int的地址: vminherit::vbase_right::_base_right_int 0x7fffcded6178
对象vminherit的实例b的成员变量vbase_right::_base_right_ch的地址: vminherit::vbase_right::_base_right_ch 0x7fffcded617c
对象vminherit的实例b的成员变量_minherit_ch的地址: vminherit::_minherit_ch 0x7fffcded617d
第一个虚函数表:
对象vminherit的第1个虚函数表地址: vminherit::vptr 0x7fffcded6160
第0个虚函数执行结果: vminherit::vbase_left_virtual_function1
对象vminherit的第0个虚函数地址: 0x7f1d216b39ce
第1个虚函数执行结果: vbase_left::vbase_left_virtual_function2
对象vminherit的第1个虚函数地址: 0x7f1d216b3926
第2个虚函数执行结果: vminherit::vminherit_virtual_function
对象vminherit的第2个虚函数地址: 0x7f1d216b3a44
第3个虚函数执行结果: vminherit::vbase_right_virtual_function1
对象vminherit的第3个虚函数地址: 0x7f1d216b3a06
第二个虚函数表:
对象vminherit的第2个虚函数表地址: vminherit::vptr 0x7fffcded6170
第0个虚函数执行结果: vminherit::vbase_right_virtual_function1
对象vminherit的第0个虚函数地址: 0x7f1d216b3a3d
第1个虚函数执行结果: vbase_right::vbase_right_virtual_function2
对象vminherit的第1个虚函数地址: 0x7f1d216b3996
从上面的测试结果能够看到第一个虚函数表中多了一个第二个基类的一个虚函数的副本,该虚函数是被子类重写过得,未重写的仍然在原来的表格中。
3.2.7 不包含虚函数的菱形继承
3.2.7.1 不使用虚继承
Class child
size=24 align=4
base size=22 base align=4
child (0x0x7f2f78f1e310) 0
mother (0x0x7f2f78f0d270) 0
grand (0x0x7f2f79077a80) 0
father (0x0x7f2f78f0d2d8) 12
grand (0x0x7f2f79077ae0) 12
对象child的实例b的大小为: 24
对象child的实例b的地址: 0x7fffeff730a0
对象child的实例b的成员变量father::_grand_x的地址: child::father::_grand_x 0x7fffeff730ac
对象child的实例b的成员变量father::_grand_ch的地址: child::father::_grand_ch 0x7fffeff730b0
对象child的实例b的成员变量father::_father_ch的地址: child::father::_father_ch 0x7fffeff730b4
对象child的实例b的成员变量mother::_grand_x的地址: child::mother::_grand_x 0x7fffeff730a0
对象child的实例b的成员变量mother::_grand_ch的地址: child::mother::_grand_ch 0x7fffeff730a4
对象child的实例b的成员变量mother::_mother_ch的地址: child::mother::_mother_ch 0x7fffeff730a8
对象child的实例b的成员变量_child_ch的地址: child::_child_ch 0x7fffeff730b5
内存结构基本和vs相同,只是内存紧缩的问题。
3.2.7.2 使用虚继承
Vtable for child
child::_ZTV5child: 6 entries
0 28
8 (int (*)(...))0
16 (int (*)(...))(& _ZTI5child)
24 12
32 (int (*)(...))-16
40 (int (*)(...))(& _ZTI5child)
Construction vtable for mother (0x0x7fe8eab9d270 instance) in child
child::_ZTC5child0_6mother: 3 entries
0 28
8 (int (*)(...))0
16 (int (*)(...))(& _ZTI6mother)
Construction vtable for father (0x0x7fe8eab9d2d8 instance) in child
child::_ZTC5child16_6father: 3 entries
0 12
8 (int (*)(...))0
16 (int (*)(...))(& _ZTI6father)
VTT for child
child::_ZTT5child: 4 entries
0 ((& child::_ZTV5child) + 24)
8 ((& child::_ZTC5child0_6mother) + 24)
16 ((& child::_ZTC5child16_6father) + 24)
24 ((& child::_ZTV5child) + 48)
Class child
size=40 align=8
base size=26 base align=8
child (0x0x7fe8eabae310) 0
vptridx=0 vptr=((& child::_ZTV5child) + 24)
mother (0x0x7fe8eab9d270) 0
primary-for child (0x0x7fe8eabae310)
subvttidx=8
grand (0x0x7fe8ead07a80) 28 virtual
vbaseoffset=-24
father (0x0x7fe8eab9d2d8) 16
subvttidx=16 vptridx=24 vptr=((& child::_ZTV5child) + 48)
grand (0x0x7fe8ead07a80) alternative-path
对象child的实例b的大小为: 40
对象child的实例b的地址: 0x7fffe265d4a0
对象child的实例b的成员变量father::_grand_x的地址: child::father::_grand_x 0x7fffe265d4bc
对象child的实例b的成员变量father::_grand_ch的地址: child::father::_grand_ch 0x7fffe265d4c0
对象child的实例b的成员变量father::_father_ch的地址: child::father::_father_ch 0x7fffe265d4b8
对象child的实例b的成员变量mother::_grand_x的地址: child::mother::_grand_x 0x7fffe265d4bc
对象child的实例b的成员变量mother::_grand_ch的地址: child::mother::_grand_ch 0x7fffe265d4c0
对象child的实例b的成员变量mother::_mother_ch的地址: child::mother::_mother_ch 0x7fffe265d4a8
对象child的实例b的成员变量_child_ch的地址: child::_child_ch 0x7fffe265d4b9
3.2.8 包含虚函数的菱形继承
3.2.8.1 不使用虚继承
Class vchild
size=32 align=8
base size=31 base align=8
vchild (0x0x7fc36bc6e3f0) 0
vptr=((& vchild::_ZTV6vchild) + 16)
vmother (0x0x7fc36b850208) 0
primary-for vchild (0x0x7fc36bc6e3f0)
vgrand (0x0x7fc36b833a20) 0
primary-for vmother (0x0x7fc36b850208)
vfather (0x0x7fc36b850270) 16
vptr=((& vchild::_ZTV6vchild) + 96)
vgrand (0x0x7fc36b833a80) 16
primary-for vfather (0x0x7fc36b850270)
对象vchild的实例b的大小为: 32
对象vchild的实例b的地址: 0x7fffdeff1740
对象vchild的实例b的成员变量vfather::_vgrand_x的地址: vchild::vfather::_vgrand_x 0x7fffdeff1758
对象vchild的实例b的成员变量vfather::_vgrand_ch的地址: vchild::vfather::_vgrand_ch 0x7fffdeff175c
对象vchild的实例b的成员变量vfather::_vfather_ch的地址: vchild::vfather::_vfather_ch 0x7fffdeff175d
对象vchild的实例b的成员变量vmother::_vgrand_x的地址: vchild::vmother::_vgrand_x 0x7fffdeff1748
对象vchild的实例b的成员变量vmother::_vgrand_ch的地址: vchild::vmother::_vgrand_ch 0x7fffdeff174c
对象vchild的实例b的成员变量vmother::_vmother_ch的地址: vchild::vmother::_vmother_ch 0x7fffdeff174d
对象vchild的实例b的成员变量_vchild_ch的地址: vchild::_vchild_ch 0x7fffdeff175e
第一个虚函数表:
对象vchild的第1个虚函数表地址: vchild::vptr 0x7fffdeff1740
第0个虚函数执行结果: vgrand_virtual_func1
对象vchild的第0个虚函数地址: 0x7f48bf84a242
第1个虚函数执行结果: vgrand_virtual_func2
对象vchild的第1个虚函数地址: 0x7f48bf84a4aa
第2个虚函数执行结果: vgrand_virtual_func3
对象vchild的第2个虚函数地址: 0x7f48bf84a606
第3个虚函数执行结果: vgrand_virtual_func4
对象vchild的第3个虚函数地址: 0x7f48bf84a590
第4个虚函数执行结果: vgrand_virtual_func5
对象vchild的第4个虚函数地址: 0x7f48bf84a322
第5个虚函数执行结果: vmother_virtual_func1
对象vchild的第5个虚函数地址: 0x7f48bf84a5ce
第6个虚函数执行结果: vmother_virtual_func2
对象vchild的第6个虚函数地址: 0x7f48bf84a472
第二个虚函数表:
对象vchild的第1个虚函数表地址: vchild::vptr 0x7fffdeff1750
第0个虚函数执行结果: vgrand_virtual_func1
对象vchild的第0个虚函数地址: 0x7f48bf84a3ca
第1个虚函数执行结果: vgrand_virtual_func2
对象vchild的第1个虚函数地址: 0x7f48bf84a27a
第2个虚函数执行结果: vgrand_virtual_func3
对象vchild的第2个虚函数地址: 0x7f48bf84a63d
第3个虚函数执行结果: vgrand_virtual_func4
对象vchild的第3个虚函数地址: 0x7f48bf84a5c7
第4个虚函数执行结果: vgrand_virtual_func5
对象vchild的第4个虚函数地址: 0x7f48bf84a322
第5个虚函数执行结果: vfather_virtual_func1
对象vchild的第5个虚函数地址: 0x7f48bf84a589
第6个虚函数执行结果: vfather_virtual_func2
对象vchild的第6个虚函数地址: 0x7f48bf84a392
基本的结构还是和vs差不多,只是内存紧缩。
3.2.8.2 使用虚继承
VTT for vchild
vchild::_ZTT6vchild: 7 entries
0 ((& vchild::_ZTV6vchild) + 24)
8 ((& vchild::_ZTC6vchild0_7vmother) + 24)
16 ((& vchild::_ZTC6vchild0_7vmother) + 112)
24 ((& vchild::_ZTC6vchild16_7vfather) + 24)
32 ((& vchild::_ZTC6vchild16_7vfather) + 112)
40 ((& vchild::_ZTV6vchild) + 184)
48 ((& vchild::_ZTV6vchild) + 96)
Class vchild
size=48 align=8
base size=26 base align=8
vchild (0x0x7f748bf4e3f0) 0
vptridx=0 vptr=((& vchild::_ZTV6vchild) + 24)
vmother (0x0x7f748bb302d8) 0
primary-for vchild (0x0x7f748bf4e3f0)
subvttidx=8
vgrand (0x0x7f748bb13a20) 32 virtual
vptridx=40 vbaseoffset=-24 vptr=((& vchild::_ZTV6vchild) + 184)
vfather (0x0x7f748bb30340) 16
subvttidx=24 vptridx=48 vptr=((& vchild::_ZTV6vchild) + 96)
vgrand (0x0x7f748bb13a20) alternative-path
对象vchild的实例b的大小为: 48
对象vchild的实例b的地址: 0x7ffff30b0210
对象vchild的实例b的成员变量vfather::_vgrand_x的地址: vchild::vfather::_vgrand_x 0x7ffff30b0238
对象vchild的实例b的成员变量vfather::_vgrand_ch的地址: vchild::vfather::_vgrand_ch 0x7ffff30b023c
对象vchild的实例b的成员变量vfather::_vfather_ch的地址: vchild::vfather::_vfather_ch 0x7ffff30b0228
对象vchild的实例b的成员变量vmother::_vgrand_x的地址: vchild::vmother::_vgrand_x 0x7ffff30b0238
对象vchild的实例b的成员变量vmother::_vgrand_ch的地址: vchild::vmother::_vgrand_ch 0x7ffff30b023c
对象vchild的实例b的成员变量vmother::_vmother_ch的地址: vchild::vmother::_vmother_ch 0x7ffff30b0218
对象vchild的实例b的成员变量_vchild_ch的地址: vchild::_vchild_ch 0x7ffff30b0229
第一个虚函数表:
对象vchild的第1个虚函数表地址: vchild::vptr 0x7ffff30b0210
第0个虚函数执行结果: vchild::vmother_virtual_func1
对象vchild的第0个虚函数地址: 0x7f19968aa71a
第1个虚函数执行结果: vmother::vmother_virtual_func2
对象vchild的第1个虚函数地址: 0x7f19968aa53a
第2个虚函数执行结果: vmother::vgrand_virtual_func2
对象vchild的第2个虚函数地址: 0x7f19968aa584
第3个虚函数执行结果: vchild::vgrand_virtual_func3
对象vchild的第3个虚函数地址: 0x7f19968aa764
第4个虚函数执行结果: vchild::vgrand_virtual_func4
对象vchild的第4个虚函数地址: 0x7f19968aa6c6
第5个虚函数执行结果: vchild::vfather_virtual_func1
对象vchild的第5个虚函数地址: 0x7f19968aa676
第二个虚函数表:
对象vchild的第2个虚函数表地址: vchild::vptr 0x7ffff30b0220
第0个虚函数执行结果: vchild::vfather_virtual_func1
对象vchild的第0个虚函数地址: 0x7f19968aa6c0
第1个虚函数执行结果: vfather::vfather_virtual_func2
对象vchild的第1个虚函数地址: 0x7f19968aa3fe
第2个虚函数执行结果: vfather::vgrand_virtual_func1
对象vchild的第2个虚函数地址: 0x7f19968aa448
第3个虚函数执行结果: vchild::vgrand_virtual_func3
对象vchild的第3个虚函数地址: 0x7f19968aa7b7
第三个虚函数表:
对象vchild的第3个虚函数表地址: vchild::vptr 0x7ffff30b0230
第0个虚函数执行结果: vfather::vgrand_virtual_func1
对象vchild的第0个虚函数地址: 0x7f19968aa492
第1个虚函数执行结果: vmother::vgrand_virtual_func2
对象vchild的第1个虚函数地址: 0x7f19968aa5ce
第2个虚函数执行结果: vchild::vgrand_virtual_func3
对象vchild的第2个虚函数地址: 0x7f19968aa7ae
第3个虚函数执行结果: vchild::vgrand_virtual_func4
对象vchild的第3个虚函数地址: 0x7f19968aa710
第4个虚函数执行结果: vgrand::vgrand_virtual_func5
对象vchild的第4个虚函数地址: 0x7f19968aa36a
可以看到gcc类中不存在vbptr,即用来指出基类的指针了。
3.3 Clang
clang测试版本
clang version 10.0.0-4ubuntu1
Target: x86_64-pc-linux-gnu
Thread model: posix
测试过程中会对内存模型和gcc,vs对比,如果和二者都不相同则会画出结构图,否则会说明,无说明,则表示三者都相同。
从下面的测试中将会看到,clang的处理方式和gcc类似,只是clang的函数存放在地址较低的地方。
3.3.1 简单对象
对象base的实例b的地址: 0x7fffee8e1288
对象base的实例b的成员变量_nonstatic_x的地址: base::_nonstatic_x 0x7fffee8e1288
对象base的实例b的成员变量_nonstatic_ch的地址: base::_nonstatic_ch 0x7fffee8e128c
对象base的静态成员变量_static_x的地址: base::_static_x 0x40b088
3.3.2 包含虚函数的简单对象
对象virtual_base的实例b的大小为: 16
对象virtual_base的实例b的地址: 0x7fffc80cc3e0
对象virtual_base的实例b的成员变量_nonstatic_x的地址: virtual_base::_nonstatic_x 0x7fffc80cc3e8
对象virtual_base的实例b的成员变量_nonstatic_ch的地址: virtual_base::_nonstatic_ch 0x7fffc80cc3ec
对象virtual_base的第1个虚函数表地址: virtual_base::vptr 0x7fffc80cc3e0
第0个虚函数执行结果: virtual_base::virtual_func1
对象virtual_base的第0个虚函数地址: 0x401c00
第1个虚函数执行结果: virtual_base::virtual_func2
对象virtual_base的第1个虚函数地址: 0x401c40
第2个虚函数执行结果: virtual_base::virtual_func3
对象virtual_base的第2个虚函数地址: 0x401c80
第3个虚函数执行结果: virtual_base::virtual_func4
对象virtual_base的第3个虚函数地址: 0x401cc0
3.3.3 简单单继承
对象inherit的实例b的大小为: 8
对象inherit的实例b的地址: 0x7fffddb5d388
对象inherit的实例b的成员变量base::_nonstatic_x的地址: inherit::base::_nonstatic_x 0x7fffddb5d388
对象inherit的实例b的成员变量base::_nonstatic_ch的地址: inherit::base::_nonstatic_ch 0x7fffddb5d38c
对象inherit的实例b的成员变量_inherit_ch的地址: inherit::_inherit_ch 0x7fffddb5d38d
3.3.4 包含虚函数的简单单继承
对象virtual_inherit的实例b的大小为: 16
对象virtual_inherit的实例b的地址: 0x7fffc3795c60
对象virtual_inherit的实例b的成员变量virtual_base::_nonstatic_x的地址: virtual_inherit::virtual_base::_nonstatic_x 0x7fffc3795c68
对象virtual_inherit的实例b的成员变量virtual_base::_nonstatic_ch的地址: virtual_inherit::virtual_base::_nonstatic_ch 0x7fffc3795c6c
对象virtual_inherit的实例b的成员变量_virtual_inherit_ch的地址: virtual_inherit::_virtual_inherit_ch 0x7fffc3795c6d
对象virtual_inherit的第1个虚函数表地址: virtual_inherit::vptr 0x7fffc3795c60
第0个虚函数执行结果: virtual_inherit::virtual_func1
对象virtual_base的第0个虚函数地址: 0x401d00
第1个虚函数执行结果: virtual_base::virtual_func2
对象virtual_base的第1个虚函数地址: 0x401c40
第2个虚函数执行结果: virtual_base::virtual_func3
对象virtual_base的第2个虚函数地址: 0x401c80
第3个虚函数执行结果: virtual_base::virtual_func4
对象virtual_base的第3个虚函数地址: 0x401cc0
第4个虚函数执行结果: virtual_inherit::virtual_inherit_func
对象virtual_base的第4个虚函数地址: 0x401d40
3.3.5 简单多继承
对象minherit的实例b的大小为: 20
对象minherit的实例b的地址: 0x7fffc14c2c58
对象minherit的实例b的成员变量base_left::_base_left_int的地址: minherit::base_left::_base_left_int 0x7fffc14c2c60
对象minherit的实例b的成员变量base_left::_base_left_ch的地址: minherit::base_left::_base_left_ch 0x7fffc14c2c64
对象minherit的实例b的成员变量base_right::_base_right_int的地址: minherit::base_right::_base_right_int 0x7fffc14c2c58
对象minherit的实例b的成员变量base_right::_base_right_ch的地址: minherit::base_right::_base_right_ch 0x7fffc14c2c5c
对象minherit的实例b的成员变量_minherit_ch的地址: minherit::_minherit_ch 0x7fffc14c2c68
3.3.6 包含虚函数的多继承
对象vminherit的实例b的大小为: 32
对象vminherit的实例b的地址: 0x7fffdd0afb40
对象vminherit的实例b的成员变量vbase_left::_base_left_int的地址: vminherit::vbase_left::_base_left_int 0x7fffdd0afb48
对象vminherit的实例b的成员变量vbase_left::_base_left_ch的地址: vminherit::vbase_left::_base_left_ch 0x7fffdd0afb4c
对象vminherit的实例b的成员变量vbase_right::_base_right_int的地址: vminherit::vbase_right::_base_right_int 0x7fffdd0afb58
对象vminherit的实例b的成员变量vbase_right::_base_right_ch的地址: vminherit::vbase_right::_base_right_ch 0x7fffdd0afb5c
对象vminherit的实例b的成员变量_minherit_ch的地址: vminherit::_minherit_ch 0x7fffdd0afb5d
第一个虚函数表:
对象vminherit的第1个虚函数表地址: vminherit::vptr 0x7fffdd0afb40
第0个虚函数执行结果: vminherit::vbase_left_virtual_function1
对象vminherit的第0个虚函数地址: 0x401e80
第1个虚函数执行结果: vbase_left::vbase_left_virtual_function2
对象vminherit的第1个虚函数地址: 0x401dc0
第2个虚函数执行结果: vminherit::vminherit_virtual_function
对象vminherit的第2个虚函数地址: 0x401f20
第3个虚函数执行结果: vminherit::vbase_right_virtual_function1
对象vminherit的第3个虚函数地址: 0x401ec0
第二个虚函数表:
对象vminherit的第2个虚函数表地址: vminherit::vptr 0x7fffdd0afb50
第0个虚函数执行结果: vminherit::vbase_right_virtual_function1
对象vminherit的第0个虚函数地址: 0x401f00
第1个虚函数执行结果: vbase_right::vbase_right_virtual_function2
对象vminherit的第1个虚函数地址: 0x401e40
3.3.7 不包含虚函数的菱形继承
3.3.7.1 不使用虚继承
对象child的实例b的大小为: 24
对象child的实例b的地址: 0x7fffd2016958
对象child的实例b的成员变量father::_grand_x的地址: child::father::_grand_x 0x7fffd2016964
对象child的实例b的成员变量father::_grand_ch的地址: child::father::_grand_ch 0x7fffd2016968
对象child的实例b的成员变量father::_father_ch的地址: child::father::_father_ch 0x7fffd201696c
对象child的实例b的成员变量mother::_grand_x的地址: child::mother::_grand_x 0x7fffd2016958
对象child的实例b的成员变量mother::_grand_ch的地址: child::mother::_grand_ch 0x7fffd201695c
对象child的实例b的成员变量mother::_mother_ch的地址: child::mother::_mother_ch 0x7fffd2016960
对象child的实例b的成员变量_child_ch的地址: child::_child_ch 0x7fffd201696d
3.3.7.2 使用虚继承
对象child的实例b的大小为: 40
对象child的实例b的地址: 0x7ffffb828c28
对象child的实例b的成员变量father::_grand_x的地址: child::father::_grand_x 0x7ffffb828c44
对象child的实例b的成员变量father::_grand_ch的地址: child::father::_grand_ch 0x7ffffb828c48
对象child的实例b的成员变量father::_father_ch的地址: child::father::_father_ch 0x7ffffb828c40
对象child的实例b的成员变量mother::_grand_x的地址: child::mother::_grand_x 0x7ffffb828c44
对象child的实例b的成员变量mother::_grand_ch的地址: child::mother::_grand_ch 0x7ffffb828c48
对象child的实例b的成员变量mother::_mother_ch的地址: child::mother::_mother_ch 0x7ffffb828c30
对象child的实例b的成员变量_child_ch的地址: child::_child_ch 0x7ffffb828c41
3.3.8 包含虚函数的菱形继承
3.3.8.1 不使用虚继承
对象vchild的实例b的大小为: 32
对象vchild的实例b的地址: 0x7ffffa3dba50
对象vchild的实例b的成员变量vfather::_vgrand_x的地址: vchild::vfather::_vgrand_x 0x7ffffa3dba68
对象vchild的实例b的成员变量vfather::_vgrand_ch的地址: vchild::vfather::_vgrand_ch 0x7ffffa3dba6c
对象vchild的实例b的成员变量vfather::_vfather_ch的地址: vchild::vfather::_vfather_ch 0x7ffffa3dba6d
对象vchild的实例b的成员变量vmother::_vgrand_x的地址: vchild::vmother::_vgrand_x 0x7ffffa3dba58
对象vchild的实例b的成员变量vmother::_vgrand_ch的地址: vchild::vmother::_vgrand_ch 0x7ffffa3dba5c
对象vchild的实例b的成员变量vmother::_vmother_ch的地址: vchild::vmother::_vmother_ch 0x7ffffa3dba5d
对象vchild的实例b的成员变量_vchild_ch的地址: vchild::_vchild_ch 0x7ffffa3dba6e
第一个虚函数表:
对象vchild的第1个虚函数表地址: vchild::vptr 0x7ffffa3dba50
第0个虚函数执行结果: vgrand::vgrand_virtual_func1
对象vchild的第0个虚函数地址: 0x4013f0
第1个虚函数执行结果: vmother::vgrand_virtual_func2
对象vchild的第1个虚函数地址: 0x401810
第2个虚函数执行结果: vchild::vgrand_virtual_func3
对象vchild的第2个虚函数地址: 0x401a90
第3个虚函数执行结果: vchild::vgrand_virtual_func4
对象vchild的第3个虚函数地址: 0x4019b0
第4个虚函数执行结果: vgrand::vgrand_virtual_func5
对象vchild的第4个虚函数地址: 0x401570
第5个虚函数执行结果: vchild::vmother_virtual_func1
对象vchild的第5个虚函数地址: 0x401a30
第6个虚函数执行结果: vmother::vmother_virtual_func2
对象vchild的第6个虚函数地址: 0x4017b0
第二个虚函数表:
对象vchild的第1个虚函数表地址: vchild::vptr 0x7ffffa3dba60
第0个虚函数执行结果: vfather::vgrand_virtual_func1
对象vchild的第0个虚函数地址: 0x401690
第1个虚函数执行结果: vgrand::vgrand_virtual_func2
对象vchild的第1个虚函数地址: 0x401450
第2个虚函数执行结果: vchild::vgrand_virtual_func3
对象vchild的第2个虚函数地址: 0x401af0
第3个虚函数执行结果: vchild::vgrand_virtual_func4
对象vchild的第3个虚函数地址: 0x401a10
第4个虚函数执行结果: vgrand::vgrand_virtual_func5
对象vchild的第4个虚函数地址: 0x401570
第5个虚函数执行结果: vchild::vfather_virtual_func1
对象vchild的第5个虚函数地址: 0x401990
第6个虚函数执行结果: vfather::vfather_virtual_func2
3.3.8.2 使用虚继承
对象vchild的实例b的大小为: 48
对象vchild的实例b的地址: 0x7fffe5bb37e0
对象vchild的实例b的成员变量vfather::_vgrand_x的地址: vchild::vfather::_vgrand_x 0x7fffe5bb3808
对象vchild的实例b的成员变量vfather::_vgrand_ch的地址: vchild::vfather::_vgrand_ch 0x7fffe5bb380c
对象vchild的实例b的成员变量vfather::_vfather_ch的地址: vchild::vfather::_vfather_ch 0x7fffe5bb37f8
对象vchild的实例b的成员变量vmother::_vgrand_x的地址: vchild::vmother::_vgrand_x 0x7fffe5bb3808
对象vchild的实例b的成员变量vmother::_vgrand_ch的地址: vchild::vmother::_vgrand_ch 0x7fffe5bb380c
对象vchild的实例b的成员变量vmother::_vmother_ch的地址: vchild::vmother::_vmother_ch 0x7fffe5bb37e8
对象vchild的实例b的成员变量_vchild_ch的地址: vchild::_vchild_ch 0x7fffe5bb37f9
第一个虚函数表:
对象vchild的第1个虚函数表地址: vchild::vptr 0x7fffe5bb37e0
第0个虚函数执行结果: vchild::vmother_virtual_func1
对象vchild的第0个虚函数地址: 0x401ab0
第1个虚函数执行结果: vmother::vmother_virtual_func2
对象vchild的第1个虚函数地址: 0x4017f0
第2个虚函数执行结果: vmother::vgrand_virtual_func2
对象vchild的第2个虚函数地址: 0x401850
第3个虚函数执行结果: vchild::vgrand_virtual_func3
对象vchild的第3个虚函数地址: 0x401b10
第4个虚函数执行结果: vchild::vgrand_virtual_func4
对象vchild的第4个虚函数地址: 0x401a30
第5个虚函数执行结果: vchild::vfather_virtual_func1
对象vchild的第5个虚函数地址: 0x4019b0
第二个虚函数表:
对象vchild的第2个虚函数表地址: vchild::vptr 0x7fffe5bb37f0
第0个虚函数执行结果: vchild::vfather_virtual_func1
对象vchild的第0个虚函数地址: 0x401a10
第1个虚函数执行结果: vfather::vfather_virtual_func2
对象vchild的第1个虚函数地址: 0x401630
第2个虚函数执行结果: vfather::vgrand_virtual_func1
对象vchild的第2个虚函数地址: 0x401690
第3个虚函数执行结果: vchild::vgrand_virtual_func3
对象vchild的第3个虚函数地址: 0x401b70
第三个虚函数表:
对象vchild的第3个虚函数表地址: vchild::vptr 0x7fffe5bb3800
第0个虚函数执行结果: vfather::vgrand_virtual_func1
对象vchild的第0个虚函数地址: 0x4016f0
第1个虚函数执行结果: vmother::vgrand_virtual_func2
对象vchild的第1个虚函数地址: 0x4018b0
第2个虚函数执行结果: vchild::vgrand_virtual_func3
对象vchild的第2个虚函数地址: 0x401b90
第3个虚函数执行结果: vchild::vgrand_virtual_func4
对象vchild的第3个虚函数地址: 0x401a90
第4个虚函数执行结果: vgrand::vgrand_virtual_func5
对象vchild的第4个虚函数地址: 0x401570
3.4 总结
三个编译器vs
(实际上为cl.exe
,此处简称vs
大家都好明白),clang,gcc
对内存对象布局大体类似。其中clang
和gcc
完全相同,只是函数地址存放位置clang
存放在地址较低的地方,并且clang
和gcc
都会对对象做内存紧缩,这样会破坏对于c的数据结构完整性兼容。
vs
和clang,gcc
的主要区别:
clang
和gcc
会做内存紧缩,vs
并没有做;clang
和gcc
在- 在包含虚函数表的菱形继承中,
vs
会保留vfptr,vbptr,
分别指向虚函数表和共享基类的偏移,而clang,gcc
只保留了vfptr
,应该在其他地方存储了偏移。 - 在包含虚函数表的菱形继承中,
vs
和其他两个编译器生成的对象的虚函数表中的内容不同; - 在包含虚函数的多继承中,
gcc
和clang
在第一个虚函数表多维护了一个被子类重写的虚函数的复本,不知为何。