Указатель на вопрос участника - PullRequest
7 голосов
/ 17 августа 2010

$ 4.11 / 2 состояния -

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

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

Ответы [ 3 ]

5 голосов
/ 17 августа 2010

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

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.

1 голос
/ 17 августа 2010

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

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

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

0 голосов
/ 17 августа 2010

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

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