首页 > 解决方案 > 为什么在这个例子中需要后期绑定?

问题描述

我理解为什么动态创建子类的对象时需要覆盖虚拟关键字,但是在下面的示例中,为什么需要后期绑定(虚拟关键字)来覆盖?编译器不能在编译时告诉 pa 指向派生类吗?

class A { 
    public: int f() { return 'A';}
};

class B : public A {
    public: int f() { return 'B';} 
};

int main() {

    //I understand this case, since pa is pointing to 
    //something created at runtime, virtual keyword is needed
    //to return 66 
    A* pa;
    pa = new B;
    cout << pa->f() << endl; //returns 65 

    //But why in this case where b2 is created during 
    //compile time, compiler doesn't know pa is pointing
    //to a derived class? 
    B b2; 
    pa = &b2; 
    cout << pa->f() << endl; //returns 65
}

标签: c++ooppointersbindingcompiler-construction

解决方案


这个问题实际上并不围绕编译器是否可以“知道”对象pa所指的精确类型。它围绕 C++ 的语义展开。

当您声明一个方法f时,您是在告诉编译器您希望如何f处理调用。在A::f未声明的情况下virtual,您是说 ifpa->f()被调用并pa具有声明的类型A*,您希望编译器使用A::f. 当然,*pa是一个类型的对象A,即使它也可能是某个派生类型的对象A

另一方面,如果您声明fvirtual,则您告诉编译器您希望它引用当前指向的对象的最派生类型pa,并使用f来自该类型(或其适当的超类型)的定义。

C++ 的语义需要是确定性的。也就是说,作为程序员,您需要能够预测f在任何给定情况下将使用哪个定义。如果您考虑一下,用一种规则编写语言真的很困难,该规则规定“B::f如果您碰巧能够找出pa指向类型的对象,则B使用,但A::f如果您不确定pa指向的对象,请使用”。编译器应该如何努力找出pa指向什么?如果将来编译器团队中的某个人想出如何做出更准确的判断,你的程序的语义是否应该神奇地改变?你会为此感到高兴吗?

请注意,在您呈现的两个片段中,编译器实际上很有可能找出指向的对象的类型是什么pa。所以即使fvirtual,优化编译器也可以省略在 vtable 中查找正确方法的代码,而直接调用正确的方法。C++ 标准中没有任何内容禁止这种优化,我相信它很常见。因此,如果将来编译器团队中的某个人找到了一种更好的方法来确定变量的类型,它不会改变程序的语义——它仍然会调用相同的方法。但这可能会导致您的程序更快地调用您的方法。这是一个更有可能让你未来快乐的结果。


推荐阅读