1

我有以下在 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;
}

这是如何运作的?

4

4 回答 4

3

一个C实例有一个A子对象和一个B子对象。
像这样的东西:

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

现在,

A *pa = &c;

pa指向A子对象的位置,并且

B *pb = &c;

pb指向B子对象的位置。

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

当您比较papb&c,会发生同样的事情 - 在第一种情况下,&cA子对象的位置,而在第二种情况下,它是子对象的位置B
所以它们都比较等于的原因&c是表达式在比较中&c实际上具有不同的值(和不同的类型)。

当您 时reinterpret_cast,不会发生任何调整 - 这意味着“获取此值的表示并将其解释为表示不同类型的值”。
由于子对象位于不同的位置,将它们重新解释为 a 的位置的结果char也不同。

于 2016-02-23T09:26:44.357 回答
3

pa并且pb实际上是不同的。一种测试方法是:

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

pa == &c并且pb == &c两者都返回true,但这并不意味着上面一定是true&c将通过隐式指针转换转换为适当的指针类型(A*或)。B*此转换将指针的值更改为 指向的对象的相应基类子对象的地址&c

cppreference

指向(可选 cv 限定的)派生类类型的纯右值指针可以转换为指向其可访问的、明确的(相同的 cv 限定的)基类的纯右值指针。转换的结果是指向指向对象内的基类子对象的指针。空指针值转换为目标类型的空指针值。

(强调我的)


A是 的第一个非虚基类C,所以直接放在C的内存空间的开头,即:

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

true。但是,B子对象布局在 之后A,所以不可能满足上述条件。两者都隐式转换,static_cast然后为您提供基子对象的正确地址。

于 2016-02-23T09:11:25.483 回答
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 的地址范围内。

于 2016-02-23T09:12:03.273 回答
0

您正在比较地址papb直接指向,它们是不同的,因为AB都是 的基类,C并且pa指向 的基类子对象A,指向 的基类子对象,实际内存地址会有所不同。它们不能/不应该指向相同的内存地址。cpbBc

于 2016-02-23T09:15:08.307 回答