What is the purpose of the “final” keyword in C++1

2020-01-29 03:09发布

What is the purpose of the final keyword in C++11 for functions? I understand it prevents function overriding by derived classes, but if this is the case, then isn't it enough to declare as non-virtual your final functions? Is there another thing I'm missing here?

标签: c++ c++11 final
10条回答
家丑人穷心不美
2楼-- · 2020-01-29 03:47

"final" also allows a compiler optimization to bypass the indirect call:

class IAbstract
{
public:
  virtual void DoSomething() = 0;
};

class CDerived : public IAbstract
{
  void DoSomething() final { m_x = 1 ; }

  void Blah( void ) { DoSomething(); }

};

with "final", the compiler can call CDerived::DoSomething() directly from within Blah(), or even inline. Without it, it has to generate an indirect call inside of Blah() because Blah() could be called inside a derived class which has overridden DoSomething().

查看更多
来,给爷笑一个
3楼-- · 2020-01-29 03:51

The final keyword allows you to declare a virtual method, override it N times, and then mandate that 'this can no longer be overridden'. It would be useful in restricting use of your derived class, so that you can say "I know my super class lets you override this, but if you want to derive from me, you can't!".

struct Foo
{
   virtual void DoStuff();
}

struct Bar : public Foo
{
   void DoStuff() final;
}

struct Babar : public Bar
{
   void DoStuff(); // error!
}

As other posters pointed out, it cannot be applied to non-virtual functions.

One purpose of the final keyword is to prevent accidental overriding of a method. In my example, DoStuff() may have been a helper function that the derived class simply needs to rename to get correct behavior. Without final, the error would not be discovered until testing.

查看更多
成全新的幸福
4楼-- · 2020-01-29 03:51

Supplement to Mario Knezović 's answer:

class IA
{
public:
  virtual int getNum() const = 0;
};

class BaseA : public IA
{
public:
 inline virtual int getNum() const final {return ...};
};

class ImplA : public BaseA {...};

IA* pa = ...;
...
ImplA* impla = static_cast<ImplA*>(pa);

//the following line should cause compiler to use the inlined function BaseA::getNum(), 
//instead of dynamic binding (via vtable or something).
//any class/subclass of BaseA will benefit from it

int n = impla->getNum();

The above code shows the theory, but not actually tested on real compilers. Much appreciated if anyone paste a disassembled output.

查看更多
放我归山
5楼-- · 2020-01-29 03:53

Final cannot be applied to non-virtual functions.

error: only virtual member functions can be marked 'final'

It wouldn't be very meaningful to be able to mark a non-virtual method as 'final'. Given

struct A { void foo(); };
struct B : public A { void foo(); };
A * a = new B;
a -> foo(); // this will call A :: foo anyway, regardless of whether there is a B::foo

a->foo() will always call A::foo.

But, if A::foo was virtual, then B::foo would override it. This might be undesirable, and hence it would make sense to make the virtual function final.

The question is though, why allow final on virtual functions. If you have a deep hierarchy:

struct A            { virtual void foo(); };
struct B : public A { virtual void foo(); };
struct C : public B { virtual void foo() final; };
struct D : public C { /* cannot override foo */ };

Then the final puts a 'floor' on how much overriding can be done. Other classes can extend A and B and override their foo, but it a class extends C then it is not allowed.

So it probably doesn't make sense to make the 'top-level' foo final, but it might make sense lower down.

(I think though, there is room to extend the words final and override to non-virtual members. They would have a different meaning though.)

查看更多
贼婆χ
6楼-- · 2020-01-29 03:55

A use-case for the 'final' keyword that I am fond of is as follows:

// This pure abstract interface creates a way
// for unit test suites to stub-out Foo objects
class FooInterface
{
public:
   virtual void DoSomething() = 0;
private:
   virtual void DoSomethingImpl() = 0;
};

// Implement Non-Virtual Interface Pattern in FooBase using final
// (Alternatively implement the Template Pattern in FooBase using final)
class FooBase : public FooInterface
{
public:
    virtual void DoSomething() final { DoFirst(); DoSomethingImpl(); DoLast(); }
private:
    virtual void DoSomethingImpl() { /* left for derived classes to customize */ }
    void DoFirst(); // no derived customization allowed here
    void DoLast(); // no derived customization allowed here either
};

// Feel secure knowing that unit test suites can stub you out at the FooInterface level
// if necessary
// Feel doubly secure knowing that your children cannot violate your Template Pattern
// When DoSomething is called from a FooBase * you know without a doubt that
// DoFirst will execute before DoSomethingImpl, and DoLast will execute after.
class FooDerived : public FooBase
{
private:
    virtual void DoSomethingImpl() {/* customize DoSomething at this location */}
};
查看更多
放我归山
7楼-- · 2020-01-29 03:57

final adds an explicit intent to not have your function overridden, and will cause a compiler error should this be violated:

struct A {
    virtual int foo(); // #1
};
struct B : A {
    int foo();
};

As the code stands, it compiles, and B::foo overrides A::foo. B::foo is also virtual, by the way. However, if we change #1 to virtual int foo() final, then this is a compiler error, and we are not allowed to override A::foo any further in derived classes.

Note that this does not allow us to "reopen" a new hierarchy, i.e. there's no way to make B::foo a new, unrelated function that can be independently at the head of a new virtual hierarchy. Once a function is final, it can never be declared again in any derived class.

查看更多
登录 后发表回答