類布局
代碼 |
類布局 |
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 ’。
- 虛函數不會增加對象體積吧,以前怎么會有這個觀念...