C++ 类的各个成员在内存中的布局,一般是通过理论推导和各种旁敲侧击的手段来大致得出的。实际上,我们可以获得更全面,更确信的材料:让编译器直接告诉我们某个类在内存中的实际排布。
MSVC
在 Visual Studio 中添加 /d1 reportAllClassLayout
或 /d1 reportSingleClassLayout<类名>
这两个编译参数:前者打印所有类,后者打印某一个类。以此代码为例:
1class Base {
2public:
3 Base() : a(0) {}
4 virtual ~Base() {};
5
6 virtual void Func1() {};
7 virtual void Func2() {};
8
9 void Func3() {};
10private:
11 int a;
12};
13
14class Sub : public Base {
15public:
16 Sub() : b(0) {}
17 ~Sub() {};
18
19 virtual void Func1() {};
20
21 void Func4() {};
22private:
23 int b;
24};
编译后,类的内存布局会作为编译信息的一部分输出到标准输出,并以字符画的形式呈现:
1class Base size(8):
2 +---
3 0 | {vfptr}
4 4 | a
5 +---
6
7Base::$vftable@:
8 | &Base_meta
9 | 0
10 0 | &Base::{dtor}
11 1 | &Base::Func1
12 2 | &Base::Func2
13
14Base::{dtor} this adjustor: 0
15Base::Func1 this adjustor: 0
16Base::Func2 this adjustor: 0
17Base::__delDtor this adjustor: 0
18Base::__vecDelDtor this adjustor: 0
19
20class Sub size(12):
21 +---
22 0 | +--- (base class Base)
23 0 | | {vfptr}
24 4 | | a
25 | +---
26 8 | b
27 +---
28
29Sub::$vftable@:
30 | &Sub_meta
31 | 0
32 0 | &Sub::{dtor}
33 1 | &Sub::Func1
34 2 | &Base::Func2
35
36Sub::{dtor} this adjustor: 0
37Sub::Func1 this adjustor: 0
38Sub::__delDtor this adjustor: 0
39Sub::__vecDelDtor this adjustor: 0
相较而言最为清晰直观。其他操作系统的用户可借助在线的编译平台(如 GCC Godbolt )来体验。
参考:《Visual Studio 打印类的内存布局》 ,源自 imJaron 的 CSDN 博客
GCC
1# GCC 版本 < 8
2g++-7 -c -fdump-class-hierarchy ./main.cpp
3# GCC 版本 >= 8
4g++-8 -c -fdump-lang-class ./main.cpp
两者都会生成 main.cpp.xxx.class
文件:
1Vtable for Base
2Base::_ZTV4Base: 6 entries
30 (int (*)(...))0
48 (int (*)(...))(& _ZTI4Base)
516 (int (*)(...))Base::~Base
624 (int (*)(...))Base::~Base
732 (int (*)(...))Base::Func1
840 (int (*)(...))Base::Func2
9
10Class Base
11 size=16 align=8
12 base size=12 base align=8
13Base (0x0x7ffcd48d4960) 0
14 vptr=((& Base::_ZTV4Base) + 16)
15
16Vtable for Sub
17Sub::_ZTV3Sub: 6 entries
180 (int (*)(...))0
198 (int (*)(...))(& _ZTI3Sub)
2016 (int (*)(...))Sub::~Sub
2124 (int (*)(...))Sub::~Sub
2232 (int (*)(...))Sub::Func1
2340 (int (*)(...))Base::Func2
24
25Class Sub
26 size=16 align=8
27 base size=16 base align=8
28Sub (0x0x7ffcd477d1a0) 0
29 vptr=((& Sub::_ZTV3Sub) + 16)
30 Base (0x0x7ffcd48d4e40) 0
31 primary-for Sub (0x0x7ffcd477d1a0)
Emm……不太好解读。虚函数表比较详细,但似乎没有成员变量的信息。
参考:GCC online documentation for releases 7.5 and 8.4 .
Clang
注意:上述代码只有类的定义,在这里需要作出修改,要包含程序实际执行的部分,并且在执行流中使用到要打印的类,否则无法生成该类的内存布局。
1# 只尝试了 clang-7 和 clang-9
2clang-7 -cc1 -emit-llvm -fdump-record-layouts ./main.cpp
3clang-7 -cc1 -emit-llvm -fdump-vtable-layouts ./main.cpp
内存排布信息输出在标准输出,比较长也比较复杂,这里就不贴了。
参考:
-
Stack Overflow 上的这个回答
-
Dumping a C++ object’s memory layout with Clang on Eli Bendersky’s website