了解虚拟继承类vtable和vptr创建

时间:2018-09-20 17:11:13

标签: c++ virtual-functions vtable virtual-inheritance vptr

下面的代码具有多重继承性,其中每个类都有一个成员变量,一个普通函数和一个虚函数。

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);
}

程序输出如下:

main()
C 8
D 16
E 16
F 28

看到gdb中的每个对象,我在下面看到:

(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”和“ derivedd”对象中,为它们的vtable添加了vptr。

问题是为什么子类尽管具有虚函数,却没有自己的vtable,为什么在对象中没有自己的vptr? 子类的虚拟功能会出现在哪个类的vtable中?

1 个答案:

答案 0 :(得分:2)

就像正常继承一样,编译器可以自由地将其放入现有vtable中的一个中。虚拟继承保证您只有一次虚拟基类。

例如clang 7和gcc 8.2都将child::xyzde()放在derivedc的{​​{1}}的vtable中。请参见“为孩子准备的餐桌”(clang 7gcc 8.2 on godbolt)。

child

如果您将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 基类更改为虚拟,如下所示,则会得到三个单独的表:

child

clang on godbolt

class child: public virtual derivedc, public virtual derivedd

如果您删除了所有虚拟继承,则会在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() 中两次按预期获得basecclang on godbolt)。

child

C++ vtables - Part 3 - Virtual Inheritance简要介绍了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() VTT