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
派生自 Base1
和 Base2
,因此它包含 Base1
和 Base2
包含的所有数据,以及所有数据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
对象的地址。 0x0045F0B9
是 B
对象的 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 子对象 Base1
和 Base2
都位于 B
对象的最开始,没有space,并与 B
分享他们的地址。
所以我有一些这样的代码:
#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
派生自 Base1
和 Base2
,因此它包含 Base1
和 Base2
包含的所有数据,以及所有数据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
对象的地址。 0x0045F0B9
是 B
对象的 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 子对象 Base1
和 Base2
都位于 B
对象的最开始,没有space,并与 B
分享他们的地址。