Почему преобразование дочернего указателя в базовый класс иногда меняет значение указателя?

Я нашел несколько других вопросов, которые, кажется, подразумевают мой вопрос, но никто не ответил на него напрямую (даже тот с почти таким же именем).

У меня есть дочерний класс C ++, полученный от двух родителей. Я создаю объект этого класса, а затем приводю его ссылку на каждого из двух его родителей. Значения приведения не совпадают, хотя одно из них остается значением дочерней ссылки. Вот код:

 class Mom
{
public:
    int a = 1;
};

class Dad
{
public:
    int b = 2;
};

class Child : public Mom, public Dad
{
public:
    int c = 3;
};

int main()
{
    Child* c = new Child; // 0x00C5A618

    Mom* m = c; // 0x00C5A618
    Dad* d = c; // 0x00C5A61C

    return 0;
}

Теперь я предполагаю, что адреса, полученные с помощью static_cast <> те из реальных субобъектов. Как оказалось, Mom является первым подобъектом, поэтому он фактически имеет тот же адрес, что и Child. Dad - это второй подобъект, поэтому он находится по другому адресу.

Насколько я понимаю, C-стиль бросает вот так

 Mom* m = (Mom*)c;
Dad* d = (Dad*)c;

будет применять static_cast <> когда это будет законно. Действительно, когда я на самом деле компилирую и выполняю эту версию, я вижу то же поведение, что и раньше.

Продолжая эту блестящую сагу, используя неявные приведения, как это

 Mom* m = c;
Dad* d = c;

также демонстрируют то же поведение, из чего я заключаю, что неявные приведения также применяют static_cast <> когда эквивалентное явное приведение будет законным.

Возвращение к Child делает то же самое:

Child* k = static_cast<Child*>(d); // 0x00C5A618

Значение в k отличается от значения в d, который был возвращен к значению, изначально указанному в c.

Я предполагаю, что в этом случае есть проверка во время выполнения, чтобы увидеть, действительно ли d указывает на подобъект Dad объекта Child.

Пока что все это имеет смысл, но я удивлен, что не существует такой корректировки адреса (если это правильно назвать) для иерархий, полученных по отдельности. То есть, если моя иерархия просто Mom -> Dad -> Child, приведя ссылку на Child в указатель на Mom или Dad никогда не изменяет значение:

class Mom
{
public:
    int a = 1;
};

class Dad : public Mom
{
public:
    int b = 2;
};

class Child : public Dad
{
public:
    int c = 3;
};

int main()
{
    Child* c = new Child; // 0x00C5A618

    Mom* m = c; // 0x00C5A618
    Dad* d = c; // 0x00C5A618

    Child* k = static_cast<Child*>(d); // 0x00C5A618

    return 0;
}

Я ожидал, что он изменится в зависимости от размера хранилища, необходимого для каждого дополнительного подкласса, или четырех байтов (размер типа int). Но они этого не делают. Все указатели в приведенном выше примере: c, m, d и k имеют одинаковое значение.

Итак, теперь я предполагаю, что объекты были размещены в памяти с объектом Mom по самому низкому адресу, дополнительное пространство, необходимое для следующего объекта Dad, и дополнительное пространство, необходимое для Child будет последним. Поскольку Dad является -a Mom, адрес Dad будет таким же, как и адрес Mom адрес, не так ли?

Аналогично, Child is-a Mom, поэтому его адрес будет таким же, как и его собственный адрес подобъекта Mom.

Итак ...

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

Possible Memory Layout for a Singly Derived Hierarchy

+-------+-----+-----+---+
| Child : Dad : Mom : a |
|       |     +-----+---+
|       |           : b |
|       +-----------+---+
|                   : c |
+-----------------------+


Possible Memory Layout for a Multiply Derived Hierarchy

+-------+-----+---+
| Child : Mom : a |
|       +-----+---+
|       : Dad : b |
|       +-----+---+
|             : c |
+-----------------+

Извините, что я немного прогулял, но мой вопрос таков: является ли тот факт, что преобразование в некоторые базовые классы объекта с множественными производными изменяет значение ссылки, является результатом того факта, что значение после преобразования адрес базового объекта в памяти?

7 голосов | спросил Stevens Miller 11 J000000Monday16 2016, 01:42:16

2 ответа


0

Да, вы правы. Обновление указателя дает адрес родительского подобъекта, который хранится где-то внутри дочернего класса. Обычно родительский подобъект сохраняется в начале дочернего объекта, поэтому указатели на дочерний и базовый подобъекты совпадают.

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

ответил Revolver_Ocelot 11 J000000Monday16 2016, 02:04:25
0
  

Я предполагаю, что в этом случае есть проверка во время выполнения, чтобы увидеть, действительно ли d указывает на подобъект Dad объекта Child.

Нет, если вы хотите проверить во время выполнения, используйте dynamic_cast (но тип источника должен быть полиморфным). С помощью static_cast компилятор просто предположит , что тип правильный, в противном случае происходит неопределенное поведение.

  

Я думаю, что это работает так: в памяти объекты следуют друг за другом так, что в чисто по отдельности иерархии выгрузка и выгрузка всегда дает один и тот же адрес

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

  

является ли тот факт, что преобразование в некоторые базовые классы объекта с множественными производными изменяет значение ссылки, является результатом того факта, что значение после преобразования является адресом базового объекта в памяти?

Да.

ответил Brian 11 J000000Monday16 2016, 02:03:42

Похожие вопросы

Популярные теги

security × 330linux × 316macos × 2827 × 268performance × 244command-line × 241sql-server × 235joomla-3.x × 222java × 189c++ × 186windows × 180cisco × 168bash × 158c# × 142gmail × 139arduino-uno × 139javascript × 134ssh × 133seo × 132mysql × 132