Указатель на вопрос участника

$ 4,11 / 2 состояния -

Rvalue типа «указатель на член B типа cv T», где B — тип класса, может быть преобразовано в rvalue типа «указатель на член D типа cv T», где D — производный класс (пункт 10) B. Если B является недоступным (пункт 11), неоднозначным (10.2) или виртуальным (10.1) базовым классом D, программа, которая требует этого преобразования, является неправильно сформированной.

Мой вопрос: почему у нас есть ограничение на то, что B не является виртуальным базовым классом D?


person Chubsdad    schedule 17.08.2010    source источник


Ответы (3)


Рассмотрим ситуацию с не виртуальным базовым классом:

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, поэтому у нас просто недостаточно информации для работы приведения вниз.

person In silico    schedule 17.08.2010

При невиртуальном наследовании члены базового класса и производного класса могут располагаться в памяти последовательно, причем сначала базовый класс, так что каждый член базового класса находится в одном и том же месте относительно адреса объекта, независимо от того, находится ли объект это B или D. Это упрощает преобразование указателя на член B в указатель на член D; оба могут быть представлены как смещение от адреса объекта.

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

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

person Mike Seymour    schedule 17.08.2010

Действительно интересный вопрос. Сегодня узнал кое-что новое. Вот что я смог найти по теме: Приведение указателей функций-членов из производного класса к виртуальному базовому классу не работает

person celavek    schedule 17.08.2010