# C++如何禁止一个类被继承
面试官问"怎么禁止一个类被继承",大多数人能答出 final,但接着追问"final 除了禁止继承还能干什么""为什么要禁止继承""C++11 之前怎么做的",就开始含糊了。
这道题考的不只是语法,而是你对面向对象设计原则的理解——什么时候该用继承,什么时候该禁止继承。
# 简要回答
C++11 起,直接用 final 修饰类即可:class MyClass final { ... };。编译器在解析继承关系时发现目标是 final 类,直接报错,零运行时开销。C++11 之前需要用私有构造函数 + 虚继承 + 友元的组合 hack,复杂且已被淘汰。
# 详细回答
final 的两种用法
第一种是修饰类:class MyClass final { ... };,禁止任何类继承它。第二种是修饰虚函数:void speak() final;,禁止派生类重写这个虚函数。两者都是编译期静态检查,不产生运行时开销。
底层原理很简单——编译器在解析继承关系或虚函数重写时,检查目标是否标记了 final。如果是,直接报编译错误 cannot derive from 'final' base 或 cannot override 'final' function,不需要任何运行时机制。
C++11 之前的替代方案(了解即可,不推荐使用)
核心思路是利用虚继承的一个特性:虚继承要求最终派生类直接调用虚基类的构造函数。具体做法是定义一个辅助类 MakeFinal,把构造函数设为 private,然后让目标类虚继承它并声明为友元。这样目标类自己能构造(因为是友元),但如果有人试图继承目标类,最终派生类需要直接调用 MakeFinal 的私有构造函数,访问不到就报错了。
这个方案依赖复杂的继承关系和访问控制,可读性差、维护成本高,现在完全没必要用了。

# 知识拓展
Q:final 在性能上有什么好处?
编译器知道一个类是 final 或者一个虚函数是 final 后,可以做**去虚拟化(devirtualization)**优化。因为不可能有派生类重写这个函数,编译器可以在编译期确定调用目标,把虚函数调用(通过 vptr 间接跳转)优化成直接调用甚至内联。热路径上频繁调用的虚函数加 final 能有明显性能提升。
Q:什么场景下应该禁止继承?
工具类(只有静态方法,没有状态,继承没意义)、策略类(通过组合使用而非继承)、资源句柄类(如 FileDescriptor,继承可能破坏 RAII 语义)、性能关键类(加 final 帮助编译器优化)。核心原则是组合优于继承——如果一个类的设计意图是被组合使用而非被继承扩展,就应该加 final 明确表达这个意图。
Q:final 和 override 有什么关系?
两者都是 C++11 引入的上下文关键字,配合虚函数使用。override 告诉编译器"我确实在重写基类虚函数",写错签名会报错,防止隐式隐藏。final 告诉编译器"到此为止,不允许再重写"。两者可以同时使用:void speak() override final; 表示"我重写了基类的 speak,并且禁止后续派生类再重写它"。
评论
验证登录状态...