所以我有一些像这样的代码:
#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编译后获得的输出:
这是B.我在地址0045F0B8
这是A. B位于地址0045F0B9
按任意键继续。 。
有人可以解释为什么地址不同吗?
答案 0 :(得分:9)
0x0045F0B8
是完整B
对象的地址。 0x0045F0B9
是Base2
对象的B
子对象的地址。
通常,完整对象的地址可能与基类子对象的地址不同。在您的情况下,B
对象可能如下所示:
+---+-------+
| | Base1 | <-- 0x0045F0B8
| B |-------+
| | Base2 | <-- 0x0045F0B9
+---+-------+
每个基类占用一个字节,Base1
之前列出Base2
。指向完整B
的指针指向开头,位于0x0045F0B8
,但指向Base2
的指针指向完整B
对象内的地址, Base2
子对象启动,即0x0045F0B9
。
然而,当我使用g ++ 4.8在我的系统上编译程序时,我得到两行中打印的相同地址。这可能是因为允许实现为空基类(所谓的空基类优化)和两个基类子对象{{分配根本没有空间 1}}和Base1
都位于Base2
对象的最开头,没有空格,并与B
共享其地址。
答案 1 :(得分:5)
B
派生自Base1
和Base2
,因此它包含Base1
和Base2
包含的所有数据,以及B
和B::bar()
包含的所有数据{1}}添加了它们。
Base2
正在将一个指向A::for()
部分的指针传递给B
,而不是B::bar()
部分。 B
正在打印A::foo()
部分的根地址,而Base2
正在打印B
部分的根地址。您传递的是同一个对象,但它们是该对象中的不同地址:
如果B
未添加任何新数据,则其基本地址可能与其最近祖先的根地址相同(由于empty base optimization):
不要依赖于此。编译器可能会在类之间添加填充,例如:
始终将各个部分视为独立部分(因为它们逻辑上是)。仅仅因为Base2
派生自B*
并不能保证指向同一对象的Base2*
指针和Base2*
指针都指向相同的内存地址。 / p>
如果您有B
指针且需要访问其dynamic_cast
数据,请使用static_cast
(或B
,如果您确定对象是{{1} }})以确保正确的B*
指针。您可以从B*
转发Base2*
而不进行投射(这就是B::bar()
能够将this
- B*
- 传递给A::foo()
的原因它期待Base2*
作为输入)。给定B*
指针,您始终可以直接访问其Base2
数据。