Рассмотрим ситуацию, связанную с не виртуальным базовым классом:
class A { int a; }
class B : public A { int b; }
class C : public A { int c; }
class D : public B, public C { int d; }
Вот возможный макет памяти:
+-------------+
| A: int a; |
+-------------+
| B: int b; |
+-------------+
| A: int a; |
+-------------+
| C: int c; |
+-------------+
| D: int d; |
+-------------+
D
заканчивается двумя A
подобъектами, поскольку он наследует от B
и C
, и каждый из них имеет подобъект A
.
Указатели на переменные-члены обычно реализуются как целочисленное смещение от начала объекта. В этом случае целочисленное смещение для int a
в объекте A
равно нулю. Таким образом, «указатель на int a
типа A
» может быть просто целочисленным смещением нуля.
Чтобы преобразовать «указатель на int a
типа A
» в «указатель на int a
типа B
», вам просто нужно целочисленное смещение к подобъекту A
, расположенному в B
(первый A
подобъект).
Чтобы преобразовать «указатель на int a
типа A
» в «указатель на int a
типа C
», вам просто нужно целочисленное смещение к подобъекту A
, расположенному в C
(второй A
подобъект).
Поскольку компилятор знает, где B
и C
относительно A
, у компилятора достаточно информации о том, как уменьшить значение с A
до B
или C
.
Теперь рассмотрим ситуацию с виртуальным базовым классом:
struct A { int a; }
struct B : virtual public A { int b; }
struct C : virtual public A { int c; }
struct D : public B, public C { int d; }
Возможное расположение памяти:
+-------------+
| B: ptr to A | ---+
| int b; | |
+-------------+ |
| C: ptr to A | ---+
| int c; | |
+-------------+ |
| D: int d; | |
+-------------+ |
| A: int a; | <--+
+-------------+
Виртуальные базовые классы, как правило, реализуются с помощью B
и C
(которые фактически являются производными от A
), которые содержат указатель на один субобъект A
. Указатели на подобъект A
являются обязательными, поскольку расположение A
относительно B
и C
не является постоянным.
Если бы у нас был только «указатель на int a
типа A
», мы не сможем привести его к «указателю на int a
типа B
», так как расположение подобъекты B
и C
могут отличаться от A
. A
не имеет обратных указателей на B
и C
, поэтому у нас просто недостаточно информации для работы downcast.