The article is originally inspired by this one: http://www.openrce.org/articles/full_view/23. The undocumented parameters in MSVC++ compiler are: /d1reportSingleClassLayout<classname> and /d1reportAllClassLayout.
A simple example:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
class CBase { int a; public: virtual void foo() { } }; class CDerived1: public CBase { int a1; public: virtual void foo1() { } }; class CDerived2: virtual public CBase { int a2; public: virtual void foo() { } virtual void foo2() { } }; |
The dumped layout:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 |
class CBase size(8): +--- 0 | {vfptr} 4 | a +--- CBase::$vftable@: | &CBase_meta | 0 0 | &CBase::foo CBase::foo this adjustor: 0 class CDerived1 size(12): +--- | +--- (base class CBase) 0 | | {vfptr} 4 | | a | +--- 8 | a1 +--- CDerived1::$vftable@: | &CDerived1_meta | 0 0 | &CBase::foo 1 | &CDerived1::foo1 CDerived1::foo1 this adjustor: 0 class CDerived2 size(20): +--- 0 | {vfptr} 4 | {vbptr} 8 | a2 +--- +--- (virtual base CBase) 12 | {vfptr} 16 | a +--- CDerived2::$vftable@CDerived2@: | &CDerived2_meta | 0 0 | &CDerived2::foo2 CDerived2::$vbtable@: 0 | -4 1 | 8 (CDerived2d(CDerived2+4)CBase) CDerived2::$vftable@CBase@: | -12 0 | &CDerived2::foo CDerived2::foo this adjustor: 12 CDerived2::foo2 this adjustor: 0 vbi: class offset o.vbptr o.vbte fVtorDisp CBase 12 4 4 0 |
You see: When using virtual inheritance, an additional vbptr is added into class layout. There is also a separated section containing the virtual base class, with vbptr pointing to it. So, the object size of virtual inheritance is bigger than non-virtual inheritance.
Now, here is a complex example:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
class CBase1 { int a1; public: virtual void foo1() { } }; class CBase2 : public virtual CBase1 { int a2; public: virtual void foo2() { } }; class CBase3 : public virtual CBase1 { int a3; public: virtual void foo3() { } }; class CBase4 : public CBase1 { int a4; public: virtual void foo4() { } }; class CDerive: public CBase2, public CBase3, public CBase4 { int b; public: virtual void bar() { } }; |
The dumped layout:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 |
class CBase1 size(8): +--- 0 | {vfptr} 4 | a1 +--- CBase1::$vftable@: | &CBase1_meta | 0 0 | &CBase1::foo1 CBase1::foo1 this adjustor: 0 class CBase2 size(20): +--- 0 | {vfptr} 4 | {vbptr} 8 | a2 +--- +--- (virtual base CBase1) 12 | {vfptr} 16 | a1 +--- CBase2::$vftable@CBase2@: | &CBase2_meta | 0 0 | &CBase2::foo2 CBase2::$vbtable@: 0 | -4 1 | 8 (CBase2d(CBase2+4)CBase1) CBase2::$vftable@CBase1@: | -12 0 | &CBase1::foo1 CBase2::foo2 this adjustor: 0 vbi: class offset o.vbptr o.vbte fVtorDisp CBase1 12 4 4 0 class CBase3 size(20): +--- 0 | {vfptr} 4 | {vbptr} 8 | a3 +--- +--- (virtual base CBase1) 12 | {vfptr} 16 | a1 +--- CBase3::$vftable@CBase3@: | &CBase3_meta | 0 0 | &CBase3::foo3 CBase3::$vbtable@: 0 | -4 1 | 8 (CBase3d(CBase3+4)CBase1) CBase3::$vftable@CBase1@: | -12 0 | &CBase1::foo1 CBase3::foo3 this adjustor: 0 vbi: class offset o.vbptr o.vbte fVtorDisp CBase1 12 4 4 0 class CBase4 size(12): +--- | +--- (base class CBase1) 0 | | {vfptr} 4 | | a1 | +--- 8 | a4 +--- CBase4::$vftable@: | &CBase4_meta | 0 0 | &CBase1::foo1 1 | &CBase4::foo4 CBase4::foo4 this adjustor: 0 class CDerive size(48): +--- | +--- (base class CBase2) 0 | | {vfptr} 4 | | {vbptr} 8 | | a2 | +--- | +--- (base class CBase3) 12 | | {vfptr} 16 | | {vbptr} 20 | | a3 | +--- | +--- (base class CBase4) | | +--- (base class CBase1) 24 | | | {vfptr} 28 | | | a1 | | +--- 32 | | a4 | +--- 36 | b +--- +--- (virtual base CBase1) 40 | {vfptr} 44 | a1 +--- CDerive::$vftable@CBase2@: | &CDerive_meta | 0 0 | &CBase2::foo2 1 | &CDerive::bar CDerive::$vftable@CBase3@: | -12 0 | &CBase3::foo3 CDerive::$vftable@: | -24 0 | &CBase1::foo1 1 | &CBase4::foo4 CDerive::$vbtable@CBase2@: 0 | -4 1 | 36 (CDerived(CBase2+4)CBase1) CDerive::$vbtable@CBase3@: 0 | -4 1 | 24 (CDerived(CBase3+4)CBase1) CDerive::$vftable@CBase1@: | -40 0 | &CBase1::foo1 CDerive::func5 this adjustor: 0 vbi: class offset o.vbptr o.vbte fVtorDisp CBase1 40 4 4 0 |
The layout of CDerive class is so complicated. First, it has 3 base classes, 1 field and 1 virtual base section. The the first 2 base classes(CBase2, CBase3) have their vbptr pointed to the address of the virtual base section.