1

У меня есть следующий код, который я запускаю в Visual Studio. Адрес c совпадает с адресом, на который указывает pa, но не совпадает с pb. Тем не менее оба тернарных оператора будут оцениваться как true, что и ожидалось, только просмотрев код и не увидев заостренных адресов для pa и pb в отладчике. Третий тернарный оператор будет оценивать как false.Как оценивать указатели и reinterpret_cast?

#include <iostream> 

class A 
{ 
public: 
    A() : m_i(0) {} 

protected: 
    int m_i; 
}; 

class B 
{ 
public: 
    B() : m_d(0.0) {} 

protected: 
    double m_d; 
}; 

class C 
    : public A 
    , public B 
{ 
public: 
    C() : m_c('a') {} 

private: 
    char m_c; 
}; 

int main() 
{ 
    C c; 
    A *pa = &c; 
    B *pb = &c; 

    const int x = (pa == &c) ? 1 : 2; 
    const int y = (pb == &c) ? 3 : 4; 
    const int z = (reinterpret_cast<char*>(pa) == reinterpret_cast<char*>(pb)) ? 5 : 6; 

    std::cout << x << y << z << std::endl; 

    return 0; 
} 

Как это работает?

+3

Ваш вопрос не соответствует примерному коду. Вы говорите «оба тройных оператора», но их три. Вы говорите «если», но его нет. –

+2

Каков ожидаемый результат вашей программы? Каков фактический результат? –

+0

@SebastianRedl: извините, третий тоже троичный. Я изменил. – codiac

ответ

3

pa и pb на самом деле разные. Один из способов проверить это:

reinterpret_cast<char*>(pa) == reinterpret_cast<char*>(pb) 

pa == &c и pb == &c как возвращение true, но это не означает, что выше, должно быть true. &c будет преобразован в соответствующий тип указателя (A* или B*) через неявное преобразование указателя. Это преобразование изменяет значение указателя на адрес соответствующего субобъекта базового класса объекта, на который указывает &c.

От cppreference:

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

(курсив мой)


A является первым невиртуальномом базового класса C, так что он будет помещен непосредственно в начале пространства памяти C «с, то есть:

reinterpret_cast<char*>(pa) == reinterpret_cast<char*>(&c) 

является true. Но, B подобъект выложен после A, поэтому он не может удовлетворить вышеуказанное условие. И неявное преобразование и static_cast затем дает вам правый адрес базового подобъекта.

1

Если вы добавите дополнительный вывод, вы увидите, что происходит; Я добавил следующую строку:

std::cout << "pa: " << pa << "; pb: " << pb << "; c: " << &c << std::endl; 

Выход этого будет меняться, конечно, так как я печать значения указателей, но это будет выглядеть так:

pa: 0x1000 pb: 0x1008 c: 0x1000 

Указатель рь в факт, указывающий на pa + sizeof (int) (который на моей 64-битной машине равен 8 байтам). Это происходит потому, что когда вы делаете:

B *pb = &c; 

Компилятор бросает объект C к B, и возвратит вас значение переменной B. Путаница в том, что ваш второй тернарный оператор показывает правду.Это (я предполагаю, что), так как адрес B находится в пределах адреса C.

0

Вы сравнение адреса pa и pb указывающих непосредственно, они отличаются, потому что A и B являются базой класс C и pa указывает на подобъект базового класса Ac, pb указывает на подобъект базового класса Bc, фактический адрес памяти будет отличаться. Они не могут/не должны указывать на один и тот же адрес памяти.

3

A C экземпляр имеет подобъект A и подобъект B.
Что-то вроде этого:

|---------| 
    |---------| 
    | A | 
    |---------| 
C: |---------| 
    | B | 
    |---------| 
    |---------| 

Теперь

A *pa = &c; 

делает pa точку в месте расположения A подобъекте и

B *pb = &c; 

делает pb точку расположения B субобъект.

|---------| 
    |---------| <------ pa 
    | A | 
    |---------| 
C: |---------| <------ pb 
    | B | 
    |---------| 
    |---------| 

Если сравнить pa и pb к &c, то же самое происходит - в первом случае, &c является расположение A подобъекте, а во втором это место B подобъекте.
Таким образом, причина, по которой они сравниваются равными &c, заключается в том, что выражение &c фактически имеет разные значения (и разные типы) в сравнении.

Когда вы reinterpret_cast, никаких корректировок не происходит - это означает «принять представление этого значения и интерпретировать его как представляющее значение другого типа».
Поскольку подобъекты находятся в разных местах, результаты их интерпретации как местоположения char также различны.

+0

Хорошие графики. Я думаю, вы забыли C в стеке. То есть, память также используется внутри класса C. – codiac