最终课程的用例

时间:2013-05-29 07:44:41

标签: c++ inheritance c++11 gotw

我正在阅读Herb Sutter的本周大师关于virtual函数的comments,最后看到他提到这个:

  

[...]“最终的使用是罕见的” - 嗯,他们有点。我不知道很多,在标准化过程中,Bjarne反复询问它解决的问题和应该使用的模式的例子,我不记得任何突出的主要问题。我唯一知道的是,如果你正在定义一个库模块(它还不是标准概念),那么使叶子类最终可以为编译器提供更多信息来虚拟化调用,因为知道库外的代码“进一步推导出来,但我不确定这些日子在整个项目优化过程中有多重要,包括积极的虚拟化。

这个答案没有提供关于类final的用例的很多例子,我很想知道它可以解决哪些问题。你知道吗,或者final课程只会变成一些模糊不清且几乎未使用的功能?

2 个答案:

答案 0 :(得分:9)

我发现一个有趣的不寻常的用例我描述了here。简而言之,通过阻止类似int类的继承,您可以在库的未来版本中使用内置类型替换它,而不会有破坏用户代码的风险。

但更常见的例子是 devirtualization 。如果将类标记为final,则编译器可以应用某些运行时优化。例如,

struct Object {
  virtual void run() = 0;
  virtual ~Object() {}
};

struct Impl final : Object
{
  void run() override {}
};

void fun(Impl & i)
{
  i.run(); // inlined!
}

由于i.run()说明符,现在可以内联对final的调用。编译器知道不需要vtable查找。

答案 1 :(得分:0)

当您为初始接口提供(某种)外观时,

final可能很有用,后者更容易被子类使用。 考虑:

class IMovable {
  public:
    void GoTo(unsigned position) = 0;
}

class Stepper : public IMovable {
  public:
    void GoTo(unsigned position) final;
  protected:
    virtual void MoveLeft() = 0;
    virtual void MoveRight() = 0;
}

void Stepper::GoTo(unsigned position) {
  for(;current_pos < position; current_pos++) {
     MoveRight(); 
  }
  for(;current_pos > position; current_pos--) {
     MoveLeft();
  } 
}       

现在,如果您想从Stepper派生,您会看到应该覆盖MoveRightMoveLeft,但不应覆盖GoTo

这个小例子很明显,但是如果IMovable有20个方法而Stepper有25个,并且有默认实现,那么你可能很难搞清楚你应该做什么以及你不应该覆盖什么。我在硬件相关的库中遇到过这样的情况。但我不认为这是一个值得通过标准来解决的主要问题;)