我有以下在Visual Studio中运行的代码。 c
的地址与点pa
的地址相同,但与pb
不同。然而,两个三元运算符都将评估为true
,这是仅通过查看代码而未在调试器中看到pa和pb
的指向地址所期望的。
第三个三元运算符将评估为false
。
#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;
}
这是如何运作的?
答案 0 :(得分:3)
pa
和pb
实际上是不同的。测试的一种方法是:
reinterpret_cast<char*>(pa) == reinterpret_cast<char*>(pb)
pa == &c
和pb == &c
都返回true
,但这并不意味着上述内容必须为true
。 &c
将通过隐式指针转换转换为适当的指针类型(A*
或B*
)。此转换将指针的值更改为&c
指向的对象的相应基类子对象的地址。
来自cppreference:
指向(可选的cv限定的)派生类类型的prvalue指针可以转换为prvalue指针,指向其可访问的,明确的(相同的cv限定的)基类。 转换的结果是指向指向对象内基类子对象的指针。空指针值将转换为目标类型的空指针值。
(强调我的)
A
是C
的第一个非虚基类,所以它直接放在C
的内存空间的开头,即:
reinterpret_cast<char*>(pa) == reinterpret_cast<char*>(&c)
是true
。但是,B
子对象在A
之后布局,因此它不可能满足上述条件。隐式转换和static_cast
都会为您提供基础子对象的正确地址。
答案 1 :(得分:3)
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
时,不进行任何调整 - 它表示&#34;采用此值的表示形式并将其解释为表示不同类型的值&#34;。
由于子对象位于不同的位置,因此将它们重新解释为char
的位置的结果也不同。
答案 2 :(得分:1)
如果你添加一些额外的输出,你可以看到发生了什么;我添加了以下行:
std::cout << "pa: " << pa << "; pb: " << pb << "; c: " << &c << std::endl;
这个的输出当然会有所不同,因为我正在打印指针的值,但它看起来像:
pa: 0x1000 pb: 0x1008 c: 0x1000
pb指针实际上指向pa + sizeof(int)(在我的64位机器上是8字节)。这是因为当你这样做时:
B *pb = &c;
编译器将C对象强制转换为B,并返回B变量的值。令人困惑的是,您的第二个三元运算符显示为真。这是(我假设),因为B的地址在C的地址范围内。
答案 3 :(得分:0)
您正在比较直接指向的地址pa
和pb
,因为A
和B
都是C
和pa
的基类{1}},A
指向c
的基类子对象pb
,B
指向{{1}的基类子对象c
1}},实际的内存地址会有所不同。他们不能/不应该指向相同的内存地址。