简单的层次结构:
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();
我在这里缺少什么?
答案 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
。