日期 更新内容
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++中的结构化数据类型structclass无法完全保证其数据存储方式能够完全按照用户预想的那样存储。
  最直观的例子是,在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_x4      | _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|  00      | &virtual_base::virtual_func11      | &virtual_base::virtual_func22      | &virtual_base::virtual_func33      | &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_x4      | | _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|  00      | &virtual_inherit::virtual_func11      | &virtual_base::virtual_func22      | &virtual_base::virtual_func33      | &virtual_base::virtual_func44      | &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_int4      | | _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_leftbase_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_int4      | | _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|  00      | &vminherit::vbase_left_virtual_function11      | &vbase_left::vbase_left_virtual_function22      | &vminherit::vminherit_virtual_functionvminherit::$vftable@vbase_right@:| -160      | &vminherit::vbase_right_virtual_function11      | &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_x4      | | | _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  onceclass 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      | 01      | 40 (childd(mother+0)grand)child::$vbtable@father@:0      | 01      | 24 (childd(father+0)grand)
vbi:       class  offset o.vbptr  o.vbte fVtorDispgrand      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的两个父类motherfather为了知道它们共享的基类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|  00      | &vgrand::vgrand_virtual_func11      | &vmother::vgrand_virtual_func22      | &vmother::vgrand_virtual_func33      | &vchild::vgrand_virtual_func44      | &vgrand::vgrand_virtual_func55      | &vchild::vmother_virtual_func16      | &vmother::vmother_virtual_func2vchild::$vftable@vfather@:| -240      | &vfather::vgrand_virtual_func11      | &vgrand::vgrand_virtual_func22      | &vfather::vgrand_virtual_func33      | &thunk: this-=24; goto vchild::vgrand_virtual_func44      | &vgrand::vgrand_virtual_func55      | &vchild::vfather_virtual_func16      | &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  onceclass 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|  00      | &vchild::vmother_virtual_func11      | &vmother::vmother_virtual_func2vchild::$vftable@vfather@:| -240      | &vchild::vfather_virtual_func11      | &vfather::vfather_virtual_func2vchild::$vbtable@vmother@:0      | -81      | 48 (vchildd(vmother+8)vgrand)vchild::$vbtable@vfather@:0      | -81      | 24 (vchildd(vfather+8)vgrand)vchild::$vftable@vgrand@:| -560      | &thunk: this-=8; goto vfather::vgrand_virtual_func11      | &thunk: this-=32; goto vmother::vgrand_virtual_func22      | &vchild::vgrand_virtual_func33      | &vchild::vgrand_virtual_func44      | &vgrand::vgrand_virtual_func5vchild::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 fVtorDispvgrand      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 basesize=8 align=4base 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_func4Class virtual_basesize=16 align=8base size=13 base align=8
virtual_base (0x0x7fb0b3ea3900) 0vptr=((& 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 inheritsize=8 align=4base size=6 base align=4
inherit (0x0x7f420c63d1a0) 0base (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_funcClass virtual_inheritsize=16 align=8base size=14 base align=8
virtual_inherit (0x0x7f8cad7b0138) 0vptr=((& virtual_inherit::_ZTV15virtual_inherit) + 16)virtual_base (0x0x7f8cad793960) 0primary-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 minheritsize=20 align=4base size=17 base align=4
minherit (0x0x7fda3561e2a0) 0base_right (0x0x7fda35777a20) 0base_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_function2Class vminheritsize=32 align=8base size=30 base align=8
vminherit (0x0x7f37ec85e380) 0vptr=((& vminherit::_ZTV9vminherit) + 16)vbase_left (0x0x7f37ec4239c0) 0primary-for vminherit (0x0x7f37ec85e380)vbase_right (0x0x7f37ec423a20) 16vptr=((& 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 childsize=24 align=4base size=22 base align=4
child (0x0x7f2f78f1e310) 0mother (0x0x7f2f78f0d270) 0grand (0x0x7f2f79077a80) 0father (0x0x7f2f78f0d2d8) 12grand (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 childsize=40 align=8base size=26 base align=8
child (0x0x7fe8eabae310) 0vptridx=0 vptr=((& child::_ZTV5child) + 24)mother (0x0x7fe8eab9d270) 0primary-for child (0x0x7fe8eabae310)subvttidx=8grand (0x0x7fe8ead07a80) 28 virtualvbaseoffset=-24father (0x0x7fe8eab9d2d8) 16subvttidx=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 vchildsize=32 align=8base size=31 base align=8
vchild (0x0x7fc36bc6e3f0) 0vptr=((& vchild::_ZTV6vchild) + 16)vmother (0x0x7fc36b850208) 0primary-for vchild (0x0x7fc36bc6e3f0)vgrand (0x0x7fc36b833a20) 0primary-for vmother (0x0x7fc36b850208)vfather (0x0x7fc36b850270) 16vptr=((& vchild::_ZTV6vchild) + 96)vgrand (0x0x7fc36b833a80) 16primary-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 vchildsize=48 align=8base size=26 base align=8
vchild (0x0x7f748bf4e3f0) 0vptridx=0 vptr=((& vchild::_ZTV6vchild) + 24)vmother (0x0x7f748bb302d8) 0primary-for vchild (0x0x7f748bf4e3f0)subvttidx=8vgrand (0x0x7f748bb13a20) 32 virtualvptridx=40 vbaseoffset=-24 vptr=((& vchild::_ZTV6vchild) + 184)vfather (0x0x7f748bb30340) 16subvttidx=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对内存对象布局大体类似。其中clanggcc完全相同,只是函数地址存放位置clang存放在地址较低的地方,并且clanggcc都会对对象做内存紧缩,这样会破坏对于c的数据结构完整性兼容。
  vsclang,gcc的主要区别:

  1. clanggcc会做内存紧缩,vs并没有做;
  2. clanggcc
  3. 在包含虚函数表的菱形继承中,vs会保留vfptr,vbptr,分别指向虚函数表和共享基类的偏移,而clang,gcc只保留了vfptr,应该在其他地方存储了偏移。
  4. 在包含虚函数表的菱形继承中,vs和其他两个编译器生成的对象的虚函数表中的内容不同;
  5. 在包含虚函数的多继承中,gccclang在第一个虚函数表多维护了一个被子类重写的虚函数的复本,不知为何。

4 参考

  • C++对象模型详解
  • C++继承类内存模型

深入探索C++对象模型之C++对象(vs,gcc,clang测试)相关推荐

  1. 深度探索C++ 对象模型(2)-类的对象的内存大小_2

    继续上文,看看继承类的大小 1. Bear类 类对象的大小为24: 16+8 class Bear : public ZooAnimal { public:Bear() {};~Bear() {}; ...

  2. 深度探索C++ 对象模型(2)-类的对象的内存大小

    1. Question: 32bit机器 1个指向地址1000的指针的大小是多少? 指针类型 涵盖地址空间 整数指针 1000~1003(32bit整数是4-bytes) void*指针 不确定 2. ...

  3. C++对象模型9——临时对象的生命周期、模板及实例化分析、内联函数

    一.临时对象的生命周期 T c=a+b 假设T是一个类型,那么上述代码执行时,首先会产生一个临时对象用来存放a+b的结果(拷贝初始化临时对象),然后用该临时对象拷贝初始化c,最后临时对象被释放.如果开 ...

  4. C++对象模型5——类对象的内存布局

    一.类对象的内存布局 1.1.单一继承的类对象布局 示例1 class base { public:int m_fai;int m_faj; }; class derive : public base ...

  5. C++对象模型1——类对象的sizeof、static成员、对象模型、this指针

    一.类对象的sizeof 1.空类对象的sizeof class test3{ };int main(int argc, char const *argv[]) { test3 t;cout<& ...

  6. C++的黑科技(深入探索C++对象模型)

    周二面了腾讯,之前只投了TST内推,貌似就是TST面试了 其中有一个问题,"如何产生一个不能被继承的类",这道题我反反复复只想到,将父类的构造函数私有,让子类不能调用,最后归结出一 ...

  7. 《深度探索C++对象模型》--5 构造析构拷贝 6 执行期语意学

     <深度探索C++对象模型>--5构造.析构.拷贝语意学 1.纯虚函数: (1)C++可以定义和调用一个纯虚函数,不过只可以静态调用,不可以由虚拟机制调用. 注意:pure virtu ...

  8. 深度探索C++ 对象模型(7)-Data member的布局(无继承、继承无多态、继承多态、多层继承)

    无继承 继承无多态 继承多态 虚表 : 用来存放基类的每一个虚函数,再加上首位的一个slots(支持RTTI). 每个class object导入一个vptr,提供执行期的链接,使得每一个class ...

  9. 深度探索C++ 对象模型(5)-Initialization list(2)

    Initialization list的作用是效率 如下代码可以编译并运行,但是效率低下 class Word {String _name;int _cnt;public:Word() { _name ...

最新文章

  1. C++ unique
  2. TX Text Control X10新特性之图像占位符合并
  3. 机器学习 美股_我如何使用机器学习来探索英美文学之间的差异
  4. 开启市场新格局 且看新华三计算与存储新品发布会
  5. Singleton 和 Monostate 模式
  6. 两个构件的重合点_GTJ2018软件中如何合并两个工程?
  7. android的app语言无法切换,Android应用实现多语言切换
  8. nginx 小简单指令
  9. ie 无人操作自动关闭_win7的IE自动关闭如何解决?
  10. 成功解决Could not fetch URL https://pypi.tuna.tsinghua.edu.cn/simple/xx/: There was a problem confirming
  11. 2021年中国研究生数学建模竞赛B题参考思路
  12. 大咖带你免费学前端,附不容错过的前端100篇文章合集
  13. 【转载】测试报告模板
  14. 什么是传统企业电商洪水围城下的诺亚方舟
  15. ucore lab1 任务六
  16. 电脑录音软件哪个比较专业
  17. QT 记住账号密码登录
  18. 彻底搞懂0-1背包问题(动态规划)
  19. 【重要补充】关于第三方潜在SDK导致的5.1.2Data use sharing
  20. GPT系列学习笔记:GPT、GPT2、GPT3

热门文章

  1. android代码修改mp3文件封面,从android中的mp3文件中提取专辑封面
  2. 布考斯基样样干_查尔斯·布考斯基经典语录
  3. 题目:利用条件运算符的嵌套来完成此题:学习成绩>=90分的同学用A表示,60-89分之间的用B表示,60分以下的用C表示。
  4. This means it will render an <Outlet /> with a null value by default resulting in an “empty“ page.
  5. 蓝懿iOS零基础学习之旅 感谢刘国斌老师
  6. 安卓逆向之双剑合璧实现内存扫描
  7. POJ 1830 开关问题 高斯消元
  8. 广九客运段铁路“姐妹花”春运真情服务获旅客点赞
  9. 「跳一跳」两年后,广告“杀死”游戏小程序?
  10. 爬虫:爬东方财富网股票数据