2012-01-28 7 views
2

Существует этот код:Виртуальное наследование и пустые виртуальные таблицы в базовом классе

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

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

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

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

+0

См. [Может существовать пустая виртуальная таблица?] (Http://stackoverflow.com/questions/7847951/can-an-empty-virtual-table-exist) – Lion

ответ

5

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

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

struct A 
{ 
    int a; 
}; 

struct B1 : virtual A 
{ 
    int b1; 
}; 

struct B2 : virtual A 
{ 
    int b2; 
}; 

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

Здесь, как и B1B2 получить практически из A, поэтому в C, есть ровно один A подобъектов. Как B1, так и B2 необходимо знать, как найти такой объект A (чтобы они могли получить доступ к переменной-члену a или другим членам 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::[email protected]: 
0  | 0 
1  | 8 (B1d(B1+0)A) 

B2::[email protected]: 
0  | 0 
1  | 8 (B2d(B2+0)A) 

C::[email protected]@: 
0  | 0 
1  | 20 (Cd(B1+0)A) 

C::[email protected]@: 
0  | 0 
1  | 12 (Cd(B2+0)A) 

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

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

+0

"смещения относительно адреса vtable" объекты? – curiousguy

+0

Почему первая запись в каждой виртуальной базовой таблице всегда 0? Фактическое смещение всегда находится во второй записи. Итак, какова цель первого? – savram

0

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

+0

@ комментарий james-mcnellis иллюстрирует, почему этот виртуальный стол выиграл Не пустее. Мой ответ показывает, почему вам нужна таблица vtable *, даже если * в противном случае она была бы пустой или такой же, как у базового класса. –

1

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

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

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

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

+0

vtable не может хранить указатели для экземпляров, поскольку это является общим для каждого объекта с заданным типом; он может хранить только смещения. – curiousguy