对于多态class的对象,对象的地址和指向对象的指针是一回事吗?
Are address of object and pointer to object the same thing for an object of polymorph class?
我正在尝试解决 C++ 测试,并看到了这个问题。
#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;
}
Output :
136
谁能解释一下它的输出?我以为base指针指向的是base部分的部分,所以不是对象的真实地址
谢谢。
pa
指向 c
的 A
子对象。 pb
指向 c
的 B
子对象。显然,它们指向内存中的不同位置(因此输出中的 6
)。
但是当它们与&c
比较时,&c
再次分别转换为A*
和B*
,从而指向相同的A
和B
子对象。
为了说明 c
在内存中的可能布局:
+------------------------+-------------+-------------------+
| A subobject | B subobject | Remainder of C |
+------------------------+-------------+-------------------+
^ &c is here ^ pb points here
^ pa also points here
背景
对象 C 在内存中看起来像这样
----------- <----- Start of the object
| A |
|---------| <----- Beginning of B implementation
| B |
|---------|
| C |
|_________| <----- End of the object
当您从派生的 class(例如 A* pa = &c)获取指向基 class 的指针时,该指针指向该 class 实现的开头那个对象。
所以这意味着A*将指向A的开头(恰好是对象的开头)而B*将指向B的开头。注意C*不会指向的开头C因为它知道C是从A和B派生出来的,它会指向对象的开头。
为什么?
因为当你调用pb->someFunction()时,它实际上是把指向B的指针加上一些预先计算好的偏移量然后执行。如果 pb 指向 A 的开头,那么它将在 A 内部结束。预先计算的偏移量是必要的,因为您不知道 pb 实际指向什么(是 C,是 "D",还是只是普通的老 B?)。这种方法允许我们始终依赖偏移量来查找函数。
这就是您的代码真正做的事情
((A*)pa == (A*)&c) // Obviously true, since we defined it as such above.
((B*)pb == (B*)&c) // Obviously true, since we defined it as such above.
(reinterpret_cast<char*>(pa) == reinterpret_cast<char*>(pb)) // We know pa and pb point to different places in memory. If we cast them both to char*, they will obviously not be equivalent.
一个有趣的尝试是
if (pa == pb)
这会给你一个编译错误,因为你需要将两个指针转换为一个通用类型。
我正在尝试解决 C++ 测试,并看到了这个问题。
#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;
}
Output :
136
谁能解释一下它的输出?我以为base指针指向的是base部分的部分,所以不是对象的真实地址
谢谢。
pa
指向 c
的 A
子对象。 pb
指向 c
的 B
子对象。显然,它们指向内存中的不同位置(因此输出中的 6
)。
但是当它们与&c
比较时,&c
再次分别转换为A*
和B*
,从而指向相同的A
和B
子对象。
为了说明 c
在内存中的可能布局:
+------------------------+-------------+-------------------+
| A subobject | B subobject | Remainder of C |
+------------------------+-------------+-------------------+
^ &c is here ^ pb points here
^ pa also points here
背景
对象 C 在内存中看起来像这样
----------- <----- Start of the object
| A |
|---------| <----- Beginning of B implementation
| B |
|---------|
| C |
|_________| <----- End of the object
当您从派生的 class(例如 A* pa = &c)获取指向基 class 的指针时,该指针指向该 class 实现的开头那个对象。
所以这意味着A*将指向A的开头(恰好是对象的开头)而B*将指向B的开头。注意C*不会指向的开头C因为它知道C是从A和B派生出来的,它会指向对象的开头。
为什么?
因为当你调用pb->someFunction()时,它实际上是把指向B的指针加上一些预先计算好的偏移量然后执行。如果 pb 指向 A 的开头,那么它将在 A 内部结束。预先计算的偏移量是必要的,因为您不知道 pb 实际指向什么(是 C,是 "D",还是只是普通的老 B?)。这种方法允许我们始终依赖偏移量来查找函数。
这就是您的代码真正做的事情
((A*)pa == (A*)&c) // Obviously true, since we defined it as such above.
((B*)pb == (B*)&c) // Obviously true, since we defined it as such above.
(reinterpret_cast<char*>(pa) == reinterpret_cast<char*>(pb)) // We know pa and pb point to different places in memory. If we cast them both to char*, they will obviously not be equivalent.
一个有趣的尝试是
if (pa == pb)
这会给你一个编译错误,因为你需要将两个指针转换为一个通用类型。