类继承和虚拟函数表

Class inheritance and the virtual function table

本文关键字:函数 虚拟 继承      更新时间:2023-10-16

一个简单的层次结构:

class X
{
public:
     virtual void test(){ printf("xn");}
};
class Y : public X
{
public:
    virtual void test() { printf("yn");}
};
class Z : public Y
{
public:
    void test() { printf("zn");}
};

如果我创建z的一个实例,我希望这个实例的vtable会指向z的测试函数,无论我将它投射到哪个基:

Z myZ;
myZ.test();
((Y)myZ).test();

我在这里错过了什么?

这是因为您对myZ进行了实际的类型转换。通过这样做,编译器将创建一个类型为Y的临时对象(Y)myZ,并使用它来调用它的test方法。

你的代码的一个更长的等价物是这样的:

Z myZ;
Y tmp = Y(myZ);
tmp.test();

您可以看到,如果test方法以任何方式修改对象,就会发生这种情况。则((Y)myZ).test()不应修改myZ

正如所指出的,动态调度只需要发生在指针或引用上。

事实上,你无法区分有指针和无指针,例如,你可以在X中使用类似的蹦床方法,尽管你使用了指针,它仍然会调用Y::test

class X {
public:
    void call_test() {
        this->test();
    }
}

动态调度不发生的事实可以被视为一种优化技术。编译器知道((Y)myZ)将具有Y的虚拟表,因此可以立即调用Y::test

((Y)myZ)

由于CCD_ 13不是指针。因此,将调用Y::test,因为这里没有关于Z的信息。

但您可以使用指针(或引用(:

   Z* z = new Z();
   z->test();
   Y* y = z;
   y->test();
   delete z;

z将打印两次。