29 December 2022

C++ 核心指南目录

C.139: Use final on classes sparingly

理由

通过 final 类限制继承层级没有什么太多逻辑上的理由,还会破坏继承层级的可扩展性。

例子

class Widget { /* ... */ };

// nobody will ever want to improve My_widget (or so you thought)
class My_widget final : public Widget { /* ... */ };

class My_improved_widget : public My_widget { /* ... */ };
// error: can't do that
error: cannot derive from 'final' base 'main()::My_widget' in derived type 'main()::My_improved_widget'
   16 | class My_improved_widget : public My_widget { /* ... */ };
      |       ^~~~~~~~~~~~~~~~~~

一开始你可能觉得不需要扩展优化My_widget,但是等到后来需要扩展的时候,就会报错。

注意

并非所有的类都用来做基类。比如大部分的标准库的类。如 std::vector , std::string 就不是用来派生子类的。这条规则也适用于那些有虚函数、用来做接口的类层级。

注意

把单个的虚函数限制为 final 也容易出错,因为人们很容易忽视 final 的存在,然后发现出错了。当然,编译器可以捕捉这类错误,所以你也不能在派生类中重新什么或重新打开一个 final 成员函数。

号称 final 能提升性能也缺少根据。通常是基于其他编程语言的经验推导出来的。

当然有时候使用 final 也有逻辑上和性能上的重要原因。一个例子是对编译器和分析语言工具来说性能关键的 AST 继承层级。新的派生类不会时不时的出现,而且新的派生类只能通过库的实现者来添加。然而,误用 final 的例子还是会更多一些。

强化

  • 标注用了 final 的类