使用虚函数进行类型转换

时间:2012-04-03 22:02:08

标签: c++ casting virtual

在下面的代码中,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”对象吗?

3 个答案:

答案 0 :(得分:6)

您的指针会看到不同的值,因为新的虚函数会导致将vtable指针注入对象。 VC ++将vtable指针放在对象的开头(这是典型的,但纯粹是内部细节)。

让我们在A中添加一个新字段,以便更容易解释。

class A {
public:
    int a;
};
// other classes unchanged

现在,在内存中,您的pAA看起来像这样:

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位系统上,这是虚拟表所需的地址大小,我相信)。