Виртуальное наследование и пустой vtable в базовом классе - PullRequest
2 голосов
/ 28 января 2012

Есть такой код:

#include <iostream>

class Base
{
   int x;
};

class Derived : virtual public Base
{
   int y;
};

int main()
{
    std::cout << sizeof(Derived) << std::endl; // prints 12
    return 0;   
}

Я читал, что когда какой-то класс виртуально наследуется, создается класс vtable для класса Derived, поэтому структура памяти выглядит следующим образом:

Derived::ptr to empty vtable
Derived::y
Base::x

и это 12 байтов.Вопрос в том, какова цель этой пустой vtable, если нет никаких виртуальных методов и как она используется?

Ответы [ 3 ]

4 голосов
/ 29 января 2012

Derived нужен какой-то способ узнать, где находится подобъект Base. При виртуальном наследовании относительное местоположение базового класса не фиксируется относительно местоположения производного класса: оно может находиться где угодно в полном объекте.

Рассмотрим более типичный пример, связанный с наследованием алмазов.

struct A
{
    int a;
};

struct B1 : virtual A
{
    int b1;
};

struct B2 : virtual A
{
    int b2;
};

struct C : B1, B2
{
    int c;
};

Здесь и B1, и B2 являются производными от A, поэтому в C имеется ровно один подобъект A. И B1, и B2 должны знать, как найти подобъект A (чтобы они могли получить доступ к переменной a member или другим членам A, если бы мы их определили).

Это то, для чего используется vtable в этом случае: и B1, и B2 будут иметь vtable, который содержит смещение подобъекта A.


Чтобы продемонстрировать, что может сделать компилятор для реализации приведенного выше примера наследования алмазов, рассмотрим следующие макеты классов и виртуальные таблицы, созданные в Visual C ++ 11 Developer Preview.

class A size(4):
        +---
 0      | a
        +---

class B1        size(12):
        +---
 0      | {vbptr}
 4      | b1
        +---
        +--- (virtual base A)
 8      | a
        +---

class B2        size(12):
        +---
 0      | {vbptr}
 4      | b2
        +---
        +--- (virtual base A)
 8      | a
        +---

class C size(24):
        +---
        | +--- (base class B1)
 0      | | {vbptr}
 4      | | b1
        | +---
        | +--- (base class B2)
 8      | | {vbptr}
12      | | b2
        | +---
16      | c
        +---
        +--- (virtual base A)
20      | a
        +---

и следующие таблицы:

B1::$vbtable@:
 0      | 0
 1      | 8 (B1d(B1+0)A)

B2::$vbtable@:
 0      | 0
 1      | 8 (B2d(B2+0)A)

C::$vbtable@B1@:
 0      | 0
 1      | 20 (Cd(B1+0)A)

C::$vbtable@B2@:
 0      | 0
 1      | 12 (Cd(B2+0)A)

Обратите внимание, что смещения относятся к адресу виртуальной таблицы, и обратите внимание, что для двух vtables, сгенерированных для подобъектов B1 и B2 в C, смещения отличаются.

(Также обратите внимание, что это полностью деталь реализации - другие компиляторы могут реализовывать виртуальные функции и базы по-разному. Этот пример демонстрирует один из способов их реализации, и они очень часто реализуются таким образом.)

1 голос
/ 29 января 2012

Для реализации виртуальных функций C ++ использует специальную форму позднего связывания , известную как виртуальная таблица .Виртуальная таблица - это таблица соответствия функций, используемая для разрешения вызовов функций в режиме динамического / позднего связывания.Виртуальная таблица иногда называется другими именами, такими как «vtable», «таблица виртуальных функций», «таблица виртуальных методов» или «таблица диспетчеризации».

Виртуальная таблица на самом деле довольно простая.Во-первых, каждый класс, который использует виртуальные функции (или является производным от класса, который использует виртуальные функции), получает свою собственную виртуальную таблицу.Эта таблица является просто статическим массивом , который компилятор устанавливает во время компиляции.Виртуальная таблица содержит одну запись для каждой виртуальной функции, которая может вызываться объектами класса.Каждая запись в этой таблице - просто указатель на функцию, которая указывает на наиболее производную функцию, доступную для этого класса.

  • Каждый класс, который использует виртуальные функции (или является производным от класса, который использует виртуальные функции)ему присваивается собственная виртуальная таблица в качестве секретного члена данных.
  • Эта таблица устанавливается компилятором во время компиляции.
  • Виртуальная таблица содержит одну запись в качестве указателя на функцию для каждой виртуальной функции.которые могут вызываться объектами класса.
  • В виртуальной таблице хранится нулевой указатель на чисто виртуальные функции.

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

0 голосов
/ 29 января 2012

Если вы выполните dynamic_cast (ptr_to_obj), он будет использовать указатель vtable, чтобы определить, ссылается ли ptr_to_obj на Derived или нет. Каждый класс, который связан с виртуальными методами или наследованием, нуждается в vtable, и он должен быть отдельным для каждого класса для поддержки dynamic_cast <>. Даже если он не содержит указателей на методы, он все равно используется для определения типа объекта.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...