类继承和虚函数表

时间:2015-10-08 07:20:48

标签: c++

简单的层次结构:

class X
{
public:
     virtual void test(){ printf("x\n");}
};

class Y : public X
{
public:
    virtual void test() { printf("y\n");}
};

class Z : public Y
{
public:
    void test() { printf("z\n");}
};

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

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

我在这里缺少什么?

2 个答案:

答案 0 :(得分:2)

((Y)myZ)

因为myZ不是指针,所以它正在切片。因此,Y::test将被调用,因为此处没有关于Z的信息。

但你可以使用指针(或引用):

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

z将被打印两次。

答案 1 :(得分:2)

这是因为您对myZ进行了实际的类型转换。通过这样做,编译器将创建一个(Y)myZ类型的临时对象Y,并将使用它来调用它的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