类布局
代码 |
类布局 |
class Base1 {
public:
void f0() {}
virtual void f1() {}
virtual void f2() {}
virtual void f3() {}
int int_in_b1;
};
class Middle2 : public Base1 {
public:
virtual void g1() {}
virtual void g2() {}
virtual void g3() {}
int int_in_b2;
};
class Top3 : public Middle2 {
public:
virtual void h1() {}
virtual void h2() {}
virtual void h3() {}
int int_in_b3;
virtual void f1() {}
};
|
-----------------------------------
class Base1 size(8):
+---
0 | {vfptr}
4 | int_in_b1
+---
Base1::$vftable@:
| &Base1_meta
| 0
0 | &Base1::f1
1 | &Base1::f2
2 | &Base1::f3
-----------------------------------
class Middle2 size(12):
+---
| +--- (base class Base1)
0 | | {vfptr}
4 | | int_in_b1
| +---
8 | int_in_b2
+---
Middle2::$vftable@:
| &Middle2_meta
| 0
0 | &Base1::f1
1 | &Base1::f2
2 | &Base1::f3
3 | &Middle2::g1
4 | &Middle2::g2
5 | &Middle2::g3
-----------------------------------
class Top3 size(16):
+---
| +--- (base class Middle2)
| | +--- (base class Base1)
0 | | | {vfptr}
4 | | | int_in_b1
| | +---
8 | | int_in_b2
| +---
12 | int_in_b3
+---
Top3::$vftable@:
| &Top3_meta
| 0
0 | &Top3::f1
1 | &Base1::f2
2 | &Base1::f3
3 | &Middle2::g1
4 | &Middle2::g2
5 | &Middle2::g3
6 | &Top3::h1
7 | &Top3::h2
8 | &Top3::h3
-----------------------------------
> |
输出虚表
Visual C++在cl编译时加上命令行"/d1 reportAllClassLayout"或"/d1 reportSingleClassLayoutXXX",XXX换成类名,会输出类的对象布局。
命令行: cl c:\foo.cpp /c /d1 reportAllClassLayout
IDE:在设置里C++命令里加上这行命令。
参考
How can one inspect a vtable in Visual C++?
Diagnosing Hidden ODR Violations in Visual C++ (and fixing LNK2022)
结论
- 对象的开始4个字节始终是虚表的地址(如果有)。这个结论在查对象被破坏、运行时崩溃等bug时很重要。
- 类只有一个虚表。基类的虚函数和自身的都会合并到一个地址里去。
- 如果继承类重载了某个虚函数,其实在虚表里是会将基类被重载的虚函数从虚表里‘抹掉’,所以你要访问基类里被重载了的虚函数,只能用“基类::函数名”访问。例如上面的‘Top3::f1 ’。
- 虚函数不会增加对象体积吧,以前怎么会有这个观念...