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