C++虚函数是否总是运行时决议?能否在编译阶段完成绑定?
关于C++虚函数决议时机的解答
问题1解答
你的理解是正确的,该场景下的虚函数调用确实可以在编译期完成决议,这是现代编译器普遍支持的*去虚化(devirtualization)*优化手段。
你给出的示例中,只要编译器能同时获取到两个信息:
print_total函数的完整实现- 调用处传入的
base、derived实例的实际静态类型
就可以直接在编译期确定item.net_price(n)应该调用的版本,不需要走运行时虚表查找逻辑。比如开启O2优化或者链接时优化(LTO)的情况下,GCC、Clang、MSVC都会对这类场景做去虚化处理,生成的汇编代码会直接调用对应类的net_price实现,和普通非虚函数调用没有性能差异。
问题2解答
C++标准从未规定虚函数必须统一在运行时决议,只要求虚函数的最终行为符合“动态绑定到指针/引用指向的对象实际类型”的语义。只要满足语义要求,编译器可以自由选择决议时机,所以虚函数完全可以在编译期完成决议。
只有当编译器无法在编译期推断出指针/引用绑定的实际类型时,才会生成运行时决议的代码,比如下面的场景就必须走运行时查找:
void func(Quote* p) { // 编译器不知道外部传入的p实际指向什么类型,只能运行时决议 p->net_price(10); }
除了你提到的场景外,还有几类典型的编译期决议场景:
- 直接通过栈对象调用虚函数,比如
base.net_price(10),对象的静态类型就是实际类型,不存在动态绑定的必要 - 调用被
final修饰的虚函数,或者final类的虚函数,编译器可以确定该函数不可能被派生类重写,直接编译期绑定 - 编译器可以明确推断指针指向的实际类型,比如
auto p = new Bulk_quote(); p->net_price(10);,这类场景也会被优化为编译期决议
《C++ Primer》提到的“可能在运行时决议”是严谨表述,只是说明虚函数存在需要运行时处理的场景,并非指代所有虚函数调用都要走运行时逻辑。
内容的提问来源于stack exchange,提问作者Jerry
相关产品推荐
相关产品推荐

