Понимание виртуальных классов наследования vtables и создания vptr - PullRequest
0 голосов
/ 20 сентября 2018

Приведенный ниже код имеет множественное наследование, где каждый класс имеет одну переменную-член, одну нормальную функцию и одну виртуальную функцию.

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" и в каждом из фактически производных классов "производных" и "производных" объектов vptrдобавлен для их vtables.

Вопрос в том, почему дочерний класс, несмотря на наличие виртуальной функции, не имеет своего собственного vtable и почему в своем объекте нет собственного vptr?В какой таблице vtable появится виртуальная функция дочернего класса?

1 Ответ

0 голосов
/ 20 сентября 2018

Компилятор может поместить его в один из существующих vtables, точно так же, как работает обычное наследование.Виртуальное наследование гарантирует, что у вас есть виртуальный базовый класс только один раз.

Например, clang 7 и gcc 8.2 помещают child::xyzde() в таблицу derivedc в child.См. «Vtable for child» ( clang 7 и gcc 8.2 на godbolt ).

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 базовые классы на виртуальные, как показано нижеполучите три отдельные таблицы:

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()

И если вы удалите все виртуальное наследование, вы получите basec дважды в childкак и ожидалось ( лязг на кресте ).

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 ++ vtables - Часть 3. Виртуальное наследование содержит краткое объяснение VTT и construction vtable for X-in-child.

...