




不能,final 不直接导致静态绑定,但为编译器提供确定性以在能静态确定动态类型且函数为 final 时进行去虚拟化优化,如内联或直接调用,而非删除 vtable 条目。

不能直接变成静态绑定,但编译器在满足条件时可以内联或消除虚表查找——前提是 final 提供了「该函数不会被重写」的确定性。没有 final,即使当前没子类,编译器也不敢假设未来不会派生并重写,因此必须保留虚调用路径。
final 类型指针/引用),且被调用函数声明为 final,编译器才可能跳过虚表查表Base* p = new Derived;),即使 Derived::func() 是 final,只要 p 类型是 Base*,仍走虚表——因为编译器不知道 p 实际指向谁-O2 及以上会结合 final 和类型信息做 devirtualization,但不是 100% 触发,需看上下文不会删掉 vtable 条目本身,但可能把该条目指向的函数体直接内联,或在特定调用点用直接地址调用替代间接跳转。vtable 结构仍存在,只是部分调用路径被绕过了。
final 不改变这一事实mov rax, [rax + offset] + call rax 变成 call _ZN6Derived3funcEv
g++ -O2 -S生成汇编验证:搜索
call 指令目标是否为具体函数符号,而非寄存器间接调用通常不会。现代编译器(GCC/Clang)默认不进行跨翻译单元的「无继承假设」,即它无法证明其他 .cpp 文件里没定义子类。链接时优化(LTO)可能带来机会,但不可靠,也不如 final 明确。
class D : public B {},编译器仍按标准要求保留虚调用语义final 是给编译器的强制契约:「此处绝无重写可能」,比运行时观察更权威final 是唯一可移植、可预期的方式几乎不会。但要注意:如果误把本应被重写的虚函数标为 final,会导致编译错误;而性能上,final 本身零开销,它只是启用优化的开关,不引入任何运行时成本。
final 就锁死了多态扩展能力,和性能无关final 是降低这部分开销的最轻量手段final 在叶节点类上收益最明显虚函数优化高度依赖具体调用上下文和编译器实现,final 不是银弹,但它把「能否优化」的决定权从编译器猜测变成了程序员声明——这点在性能敏感路径上非常关键。