考虑
virtual function
基类not overriden
中的 a 在派生类中的情况。然后使用一个base class pointer to a derived class object
虚函数被调用。
我知道函数调用将在编译时解析为基类中的函数。
问题
由于函数在派生类中没有被覆盖,函数调用会在编译时绑定到函数实现,还是会延迟绑定直到运行时?
考虑
virtual function
基类not overriden
中的 a 在派生类中的情况。然后使用一个base class pointer to a derived class object
虚函数被调用。
我知道函数调用将在编译时解析为基类中的函数。
问题
由于函数在派生类中没有被覆盖,函数调用会在编译时绑定到函数实现,还是会延迟绑定直到运行时?
很可能它会在编译时解决。
如果有足够可靠的信息供他们决定,大多数现代编译器都足够聪明,可以在编译时解决动态调度问题。
在这种情况下,由于 Derived 类中没有提供覆盖函数,因此智能编译器应该能够在编译时静态解析函数调用。
为了能够在编译时猜测实现,编译器必须知道指向对象的类型......例如
MyBaseClass *p = new MyDerivedClass;
p->foo();
在上面,编译器应该足够聪明地猜测指向对象的类型,并且即使方法是虚拟的,分派(假设编译器使用 VMT 解决方案进行后期绑定)也不需要 VMT 查找。
但是例如在下面
void doit(MyBaseClass *p)
{
p->foo();
...
}
的代码doit
无法知道指向的对象的类型,因此调用将需要 VMT 查找。请注意,C++ 语言的设计使编译器一次可以工作一个编译单元,因此编译器无法知道例如您的程序中只有一个派生类型(另一个模块的源代码可以定义一个不同的派生类,即使在函数被覆盖的本地未命名命名空间中)。
当然,该函数doit
最终可能会被编译器内联,因此如果可以推断出类型,则特定的调用站点调用doit
确实不需要查找。但是如果doit
函数是公开可见的(例如,它不在未命名的命名空间或静态自由函数中),则为它生成的机器代码将在从其他编译单元调用时包含 VMT 查找。
请注意,在所有关于何时需要查找的讨论中,如果虚函数是否已被覆盖,则完全无关紧要。原因是如果一个虚函数没有被覆盖并且调度是用 VMT 实现的,那么派生类 VMT 将在该槽中具有基实现的地址。
换句话说,发生的事情p->foo()
是编译为
p->__VMT__[__FOO_VMT_SLOT_NUMBER__](p); // Dynamic dispatch
或者
__DERIVED_FOO_IMPLEMENTATION__(p); // Type known at compile time
其中__DERIVED_FOO_IMPLEMENTATION__
是存储在 VMT 中的函数指针,它可能等于基实现的地址,也可能不等于派生类中的函数是否被覆盖。