Understanding virtual inheritance class vtables and vptr creation
下面的代码具有多重继承性,其中每个类都有一个成员变量、一个普通函数和一个虚拟函数。
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 | class basec { int x; public: basec() { x = 0; } void print() {} virtual void xyz() {} }; class derivedc: public virtual basec { int dc; public: derivedc() { dc = 0; } virtual void xyzdc() {} }; class derivedd: public virtual basec { int dd; public: derivedd() { dd = 0; } virtual void xyzdd() {} }; class child: public derivedc, public derivedd { char de; public: child() { de = '4'; } virtual void xyzde() {} }; main(int argc, char **argv) { basec bobj, bobjc; derivedc dcobj; derivedd ddobj; child deobj; std::cout <<"C" << sizeof(basec) << endl; std::cout <<"D" << sizeof(derivedc) << endl; std::cout <<"E" << sizeof(derivedd) << endl; std::cout <<"F" << sizeof(child) << endl; return(0); } |
程序输出如下:
1 2 3 4 5 | main() C 8 D 16 E 16 F 28 |
看到gdb中的每个对象,我看到如下:
1 2 3 4 5 6 7 8 9 10 11 | (gdb) p/x bobj $1 = {_vptr.basec = 0x8048c80, x = 0x0} (gdb) p/x dcobj $3 = {<basec> = {_vptr.basec = 0x8048c5c, x = 0x0}, _vptr.derivedc = 0x8048c4c, dc = 0x0} (gdb) p/x ddobj $4 = {<basec> = {_vptr.basec = 0x8048c1c, x = 0x0}, _vptr.derivedd = 0x8048c0c, dd = 0x0} (gdb) p/x deobj $5 = {<derivedc> = {<basec> = {_vptr.basec = 0x8048b90, x = 0x0}, _vptr.derivedc = 0x8048b6c, dc = 0x0}, <derivedd> = {_vptr.derivedd = 0x8048b80, dd = 0x0}, de = 0x34} |
我们看到,在基类"basec"和每个实际上派生的类"derivedc"和"derived"对象中,都为其vtables添加了vptr。
问题是,为什么不具有虚拟函数的子类没有自己的vtable,为什么对象中没有自己的vptr?子类的虚函数将出现在哪个类的vtable中?
编译器可以自由地将它放在现有vtables中的一个中,就像正常的继承工作一样。虚拟继承保证您只有一次虚拟基类。
例如,权利要求7和GCC 8.2都将
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | class derivedc : public virtual basec class derivedd : public virtual basec class child: public derivedc, public derivedd vtable for child: .quad 32 .quad 0 .quad typeinfo for child .quad derivedc::xyzdc() .quad child::xyzde() <- child::xyzde() together with derivedc's methods .quad 16 .quad -16 .quad typeinfo for child .quad derivedd::xyzdd() .quad 0 .quad -32 .quad typeinfo for child .quad basec::xyz() <- basec is only once in child |
如果将
1 | class child: public virtual derivedc, public virtual derivedd |
门闩叮当声:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | vtable for child: .quad 48 .quad 32 .quad 16 .quad 0 .quad typeinfo for child .quad child::xyzde() <- New vtable for child .quad 0 .quad 16 .quad -16 .quad typeinfo for child .quad derivedc::xyzdc() .quad 0 .quad -32 .quad typeinfo for child .quad basec::xyz() <- basec is only once in child .quad 0 .quad -16 .quad -48 .quad typeinfo for child .quad derivedd::xyzdd() |
如果你移除了所有的虚拟继承,你得到的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | class derivedc : public basec class derivedd : public basec class child: public derivedc, public derivedd vtable for child: .quad 0 .quad typeinfo for child .quad basec::xyz() <- basec from derivedc .quad derivedc::xyzdc() .quad child::xyzde() <- child::xyzde() together with derivedc's methods .quad -16 .quad typeinfo for child .quad basec::xyz() <- basec from derivedd .quad derivedd::xyzdd() |
C++ VTABLE -第3部分-虚拟继承对EDOCX1、6和EDCOX1 7的解释有一些简短的解释。