Как указатели подкласса могут строго совпадать с указателями родительского класса? - PullRequest
6 голосов
/ 29 ноября 2010

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

В приведенном ниже фрагменте, как получается, что все варианты f и b имеют одинаковое значение?

struct Foo {
    int x;
};

struct Bar : public Foo {
    int y;
};

#include <iostream>

int main ()
{
    Bar bar;

    Foo * f = &bar;
    Bar * b = &bar;
    std :: cout << f << " " << b << " " << (f == b) << "\n";

    void * fv = f;
    void * bv = b;
    std :: cout << fv << " " << bv << " " << (fv == bv) << "\n";

    int fi = reinterpret_cast <int> (f);
    int bi = reinterpret_cast <int> (b);
    std :: cout << fi << " " << bi << " " << (fi == bi) << "\n";
}

Ответы [ 6 ]

8 голосов
/ 29 ноября 2010

О единственном случае, когда объект базового класса не будет иметь тот же адрес, что и его объект подкласса, это когда задействовано множественное наследование.

В приведенном выше примере память, вероятно, выглядит следующим образом:

                               / --------- \
                              /  | x     |  > This is the Foo portion of bar
This is the whole Bar object <   --------- /  
                              \  | y     |  
                               \ ---------  

Оба вида объекта имеют одинаковую начальную точку, поэтому указатель на оба вида будет иметь одинаковое значение.

При множественном наследовании все усложняется. Скажем, у вас есть:

struct Foo1{ int x; };
struct Foo2{ int y; };
struct Bar : public Foo1, public Foo2 { int z; };
Bar bar;

Теперь память должна быть выложена примерно так:

                                / --------- \
                               /  | x     |  > This is the Foo1 portion of bar
                              /   --------- / \ 
This is the whole Bar object <    | y     |    > This is the Foo2 portion of bar  
                              \   ---------   /
                               \  | z     |  
                                \ ---------  

Так что &bar и (Foo1*)&bar будут иметь одно и то же значение, в то время как (Foo2*)&bar будет иметь другое значение, поскольку часть Foo2 объекта начинается с более высокого адреса.

2 голосов
/ 29 ноября 2010

Вообще говоря, первая часть производного класса является его родителем. Таким образом, указатель на производный и указатель на базу на одном и том же объекте часто равны. Однако это меняется при множественном наследовании. В случае MI, вообще говоря, первый кусочек производного класса это его родители в некотором, возможно, произвольном порядке (не знаю и для этого не имеет значения). Таким образом, если у вас есть указатель на производный, указатель на base1 и указатель на base2, причем производное множественное наследование от обоих, три указателя, вероятно, не все будут равны; два могут быть.

Пустота * не обязательно должна быть такой же, но обычно будет.

Значение reinterpret_cast для int одинаково, поскольку значения указателей совпадают.

2 голосов
/ 29 ноября 2010

Это типичная схема памяти, с которой вы закончите:

Bar starts   Foo starts    x starts / ends  Foo ends   y starts / ends  Bar ends
|            |             |             |         |   |             |         |

Поскольку между началом бара и началом Foo ничего нет, смещение Foo может быть нулевым, поэтому указатели для Bar, Foo и x могут быть идентичны.

0 голосов
/ 29 ноября 2010

Вы наложили f и b на одну и ту же ячейку памяти (&bar).Так что, на самом деле, я не понимаю, как можно ожидать, что они будут другими.

0 голосов
/ 29 ноября 2010

Большинство компиляторов размещают объект родительского класса в начале объекта дочернего класса, поэтому адреса совпадают, здесь никаких сюрпризов.

0 голосов
/ 29 ноября 2010

operator==() просто оценивает, указывают ли f и b на одно и то же, что в данном случае и есть.

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