在下面的代码中,pC == pA:
class A
{
};
class B : public A
{
public:
int i;
};
class C : public B
{
public:
char c;
};
int main()
{
C* pC = new C;
A* pA = (A*)pC;
return 0;
}
但是当我向B添加一个纯虚函数并在C中实现它时,pA!= pC:
class A
{
};
class B : public A
{
public:
int i;
virtual void Func() = 0;
};
class C : public B
{
public:
char c;
void Func() {}
};
int main()
{
C* pC = new C;
A* pA = (A*)pC;
return 0;
}
为什么pA在这种情况下不等于pC?难道他们两个仍然指向内存中相同的“C”对象吗?
答案 0 :(得分:6)
您的指针会看到不同的值,因为新的虚函数会导致将vtable指针注入对象。 VC ++将vtable指针放在对象的开头(这是典型的,但纯粹是内部细节)。
让我们在A中添加一个新字段,以便更容易解释。
class A {
public:
int a;
};
// other classes unchanged
现在,在内存中,您的pA
和A
看起来像这样:
pA --> | a | 0x0000004
将B和C添加到混音中后,您最终会得到:
pC --> | vtable | 0x0000000
pA --> | a | 0x0000004
| i | 0x0000008
| c | 0x000000C
正如您所看到的,pA
指向 vtable之后的数据,因为它不了解vtable或如何使用它,甚至它在那里。 pC
确实知道vtable,因此它直接指向表格,这简化了它的使用。
答案 1 :(得分:3)
指向对象的指针是 convertible 到指向基础对象的指针,反之亦然,但转换不一定非常简单。完全可能且通常是必要的,基指针具有与派生指针不同的值。这就是为什么你有一个强大的类型系统和转换。如果所有指针都相同,那么你也不需要。
答案 2 :(得分:0)
以下是我的假设,基于这个问题。
1)你有一个案例,你从C演员到A,你得到了预期的行为 2)你添加了一个虚函数,并且该投射不再有效(因为你不能再在演员之后直接从A中提取数据到A,你得到的数据对你来说没有意义)。
如果这些假设成立,那么您遇到的困难就是在B中插入虚拟表。这意味着类中的数据不再与基类中的数据完全对齐(如在类中添加的那样)字节,虚拟表,对你隐藏)。一个有趣的测试是检查sizeof以观察未知字节的增长。
要解决此问题,您不应直接从A转换为C来收集数据。您应该添加一个在A中并由B和C继承的getter函数。
鉴于您在评论中的更新,我认为您应该阅读this,它解释了虚拟表和内存布局,以及它与编译器的关系。该链接更详细地解释了我上面解释的内容,但给出了指针不同值的示例。真的,我为什么你问这个问题是错的,但似乎信息仍然是你想要的。从C到A的转换考虑了此时的虚拟表(注意C-8是4,在32位系统上,这是虚拟表所需的地址大小,我相信)。