Use cases for final classes

2019-01-24 00:10发布

I was reading comments on Herb Sutter's Guru of the Week redux about virtual functions, and finally saw him mentioning this:

[...] “uses of final are rarer” – well, they sort of are. I don’t know of many, and during standardization Bjarne repeatedly asked for examples of problems it solved and patterns where it should be used, and I don’t recall any major ones that stood out. The only one I know of offhand is that if you’re defining a library module (which isn’t a Standard concept yet) then making leaf classes final can give the compiler more information to devirtualize calls because of knowing code outside the library won’t further derive, but I’m not sure how important that is these days in the presence of whole program optimization including aggressive devirtualization.

That answer does not provide many examples about the use cases for final on classes, and I would be interested in knowing what problems it can really solve. Do you know any, or will final on classes only become some obscure and almost unused feature?

2条回答
可以哭但决不认输i
2楼-- · 2019-01-24 00:30

final might be useful when you provide a (sort of) facade to the initial interface, which is easier to use by subclasses. Consider:

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();
  } 
}       

Now if you want to derive from Stepper you see that you should override MoveRight and MoveLeft, but you shouldn't override GoTo.

It is obvious on this small example, but if IMovable had 20 methods and Stepper had 25, and there were default implementations, than you might have hard time figuring out what you should and what you shouldn't override. I have met a situation like this in a hardware-related library. But I wouldn't call this a major problem worth adressing by the standard;)

查看更多
姐就是有狂的资本
3楼-- · 2019-01-24 00:42

One interesting unusual use case I have found I described here. In short, by preventing inheritance from your int-like class, you buy yourself a possibility to replace it with a built-in type in the future releases of your library, without the risk of breaking your user's code.

But a more common example is devirtualization. If you mark your class as final, compiler can apply certain run-time optimizations. For instance,

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

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

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

The call to i.run() can be now inlined due to final specifier. The compiler knows that vtable look-up will not be needed.

查看更多
登录 后发表回答