What is the purpose of the “final” keyword in C++11 for functions?

后端 未结 10 1974
故里飘歌
故里飘歌 2020-12-02 05:31

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

相关标签:
10条回答
  • 2020-12-02 05:41

    "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().

    0 讨论(0)
  • 2020-12-02 05:46

    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.)

    0 讨论(0)
  • 2020-12-02 05:50

    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 */}
    };
    
    0 讨论(0)
  • 2020-12-02 05:52

    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.

    0 讨论(0)
  • 2020-12-02 05:53

    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.

    0 讨论(0)
  • 2020-12-02 05:54

    Nothing to add to the semantic aspects of "final".

    But I'd like to add to chris green's comment that "final" might become a very important compiler optimization technique in the not so distant future. Not only in the simple case he mentioned, but also for more complex real-world class hierarchies which can be "closed" by "final", thus allowing compilers to generate more efficient dispatching code than with the usual vtable approach.

    One key disadvantage of vtables is that for any such virtual object (assuming 64-bits on a typical Intel CPU) the pointer alone eats up 25% (8 of 64 bytes) of a cache line. In the kind of applications I enjoy to write, this hurts very badly. (And from my experience it is the #1 argument against C++ from a purist performance point of view, i.e. by C programmers.)

    In applications which require extreme performance, which is not so unusual for C++, this might indeed become awesome, not requiring to workaround this problem manually in C style or weird Template juggling.

    This technique is known as Devirtualization. A term worth remembering. :-)

    There is a great recent speech by Andrei Alexandrescu which pretty well explains how you can workaround such situations today and how "final" might be part of solving similar cases "automatically" in the future (discussed with listeners):

    http://channel9.msdn.com/Events/GoingNative/2013/Writing-Quick-Code-in-Cpp-Quickly

    0 讨论(0)
提交回复
热议问题