C ++:指针在传递后包含不同的地址

C++: Pointer contains different address after being passed

所以我有一些这样的代码:

#include <iostream>

using namespace std;

class Base1 {};
class Base2 {};

class A
{
public:
    A() {}
    void foo(Base2* ptr) { cout << "This is A. B is at the address " << ptr << endl; }
};

A *global_a;

class B : public Base1, public Base2
{
public:
    B() {}
    void bar()
    {
        cout << "This is B. I am at the address " << this << endl;
        global_a->foo(this);
    }
};

int main()
{
    global_a = new A();
    B *b = new B();

    b->bar();

    system("pause");
    return 0;
}

但这是我用 Visual Studio 2013 编译后得到的输出:

This is B. I am at the address 0045F0B8

This is A. B is at the address 0045F0B9

Press any key to continue . . .

有人可以解释为什么地址不同吗?

B 派生自 Base1Base2,因此它包含 Base1Base2 包含的所有数据,以及所有数据B 添加在它们之上。

B::bar() 将指向自身 Base2 部分的指针传递给 A::for(),而不是自身的 B 部分。 B::bar() 打印 B 部分的根地址,而 A::foo() 打印 Base2 部分的根地址。您传递的是同一个对象,但它们是该对象中的不同地址:

如果B不添加任何新数据,它的基地址可能与其最近的祖先的根地址相同(由于empty base optimization):

不要依赖那个。编译器可能会在 类 之间添加填充,例如:

始终将各个部分视为独立的(因为它们在逻辑上是独立的)。仅仅因为 B 派生自 Base2 并不能保证指向同一个对象的 B* 指针和 Base2* 指针将指向相同的内存地址。

如果您有一个 Base2* 指针并需要访问它的 B 数据,请使用 dynamic_cast(或者如果您确定对象是 static_cast =10=]) 以确保正确的 B* 指针。您可以从 B* 向下转换为 Base2* 而无需转换(这就是为什么 B::bar() 能够将 this - a B* - 传递给 A::foo() 的原因它期望 Base2* 作为输入)。给定一个 B* 指针,你总是可以直接访问它的 Base2 数据。

0x0045F0B8是完整的B对象的地址。 0x0045F0B9B 对象的 Base2 子对象的地址。

一般来说,完整对象的地址可能与基础 class 子对象的地址不同。在您的情况下,B 对象的布局可能如下所示:

+---+-------+
|   | Base1 | <-- 0x0045F0B8
| B |-------+
|   | Base2 | <-- 0x0045F0B9
+---+-------+

每个基数class占一个字节,Base1排在Base2之前。指向完整 B 的指针指向开头,即 0x0045F0B8,但指向 Base2 的指针指向完整 B 对象内部的地址Base2 子对象开始,即 0x0045F0B9.

然而,当我在我的系统上使用 g++ 4.8 编译您的程序时,我在两行中打印了相同的地址。这大概是因为允许实现分配 no space all for empty base classes (the so-called empty base class optimization) 和两个基础 class 子对象 Base1Base2 都位于 B 对象的最开始,没有space,并与 B 分享他们的地址。