C ++ 11中的“final”关键字用于函数的目的是什么?

C ++ 11中final关键字的作用是什么? 我知道它可以防止派生类重写函数,但如果是这样的话,那么声明为非虚拟的final函数还不够吗? 还有什么我在这里失踪?

你所缺less的,正如idljarn在评论中已经提到的那样,如果你正在从一个基类重写一个函数,那么你不可能把它标记为非虚拟的:

 struct base { virtual void f(); }; struct derived : base { void f() final; // virtual as it overrides base::f }; struct mostderived : derived { //void f(); // error: cannot override! }; 
  • 这是为了防止类inheritance。 维基百科 :

    C ++ 11还增加了防止从类inheritance或简单地防止派生类中的重写方法的function。 这是通过特殊标识符final来完成的。 例如:

     struct Base1 final { }; struct Derived1 : Base1 { }; // ill-formed because the class Base1 // has been marked final 
  • 它也被用来标记一个虚函数,以防止它在派生类中被覆盖:

     struct Base2 { virtual void f() final; }; struct Derived2 : Base2 { void f(); // ill-formed because the virtual function Base2::f has // been marked final }; 

维基百科还提出了一个有趣的观点 :

请注意, overridefinal都不是语言关键字。 他们在技术上是标识符; 在这些特定的环境中使用它们只会获得特殊的意义在任何其他位置,它们可以是有效的标识符。

“final”还允许编译器优化绕过间接调用:

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

使用“final”,编译器可以直接从Blah()调用CDerived::DoSomething() Blah() ,甚至可以内联。 没有它,它必须在Blah()产生一个间接调用,因为Blah()可以在重写DoSomething()的派生类中被调用。

没有什么可以添加到“最终”的语义方面。

但是我想补充一下chris green的评论,“final”在不久的将来可能成为一个非常重要的编译器优化技术 。 不仅在他提到的简单情况下,而且在更复杂的真实世界类层次结构中,可以通过“final”closures,从而使编译器能够比通常的vtable方法生成更高效的调度代码。

vtables的一个主要缺点是对于任何这样的虚拟对象(假设典型的Intel CPU上有64位),指针本身就占用了高速caching行的25%(64个字节中的8个)。 在我喜欢写的那种应用程序中,这非常糟糕。 (从我的经验来看,它是从纯粹的性能angular度来说,即C程序员对抗C ++的第一个论据。)

在需要极高性能的应用程序中,这对于C ++来说并不罕见,这可能确实变得很棒,不需要在C风格或怪异的模板中手动解决这个问题。

这种技术被称为Devirtualization 。 一个值得记住的术语。 🙂

Andrei Alexandrescu最近发表了一个很好的演讲,解释了如何在今天解决这种情况,以及“最终”可能是未来“自动”解决类似情况的一部分(与听众一起讨论):

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

Final不能应用于非虚函数。

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

能够将非虚拟方法标记为“最终”是没有意义的。 特定

 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()将总是调用A::foo

但是,如果A :: foo是virtual ,那么B :: foo将覆盖它。 这可能是不受欢迎的,因此使虚函数最终是合理的。

问题是, 为什么允许最终的虚拟function。 如果你有一个深层次的结构:

 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 */ }; 

然后, final一个“底线”,可以做多less压倒一切。 其他类可以扩展A和B并覆盖它们的foo ,但它是一个扩展C的类,那么它是不允许的。

所以把“顶级”foo作为final可能是没有意义的,但是它可能是有意义的。

(我想尽pipe如此,还是有空间把最终的话扩展到非虚拟成员,尽pipe他们有不同的含义。)

我喜欢的“最终”关键字的用例如下:

 // 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 */} }; 

final添加一个明确的意图,没有你的函数重写,并会导致一个编译器错误,如果这是违反:

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

如代码所示,它编译, B::foo覆盖A::foo 。 顺便说一句, B::foo也是虚拟的。 但是,如果将#1更改为virtual int foo() final ,那么这是一个编译器错误,我们不允许在派生类中进一步覆盖A::foo

请注意,这不允许我们“重新打开”一个新的层次结构,也就是说,无法使B::foo成为一个独立于新虚拟层次结构之上的新的无关函数。 一旦函数是最终的,它就不能再在任何派生类中声明。

最后的关键字允许你声明一个虚拟方法,重写它N次,然后强制“这个不能再被覆盖”。 这将有助于限制派生类的使用,这样你就可以说“我知道我的超类允许你重写这个,但是如果你想从我这里派生,你不能!”。

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

正如其他海报所指出的,它不适用于非虚拟function。

final关键字的一个目的是防止意外覆盖某个方法。 在我的例子中,DoStuff()可能是派生类只需要重新命名以获得正确行为的帮助函数。 没有最终的,错误将不会被发现,直到testing。

添加到函数中时,C ++中的Final关键字可防止它被基类覆盖。 另外,当添加到一个类可以防止任何types的inheritance。 考虑下面的例子,它显示了最终说明符的使用。 这个程序在编译失败。

 #include <iostream> using namespace std; class Base { public: virtual void myfun() final { cout << "myfun() in Base"; } }; class Derived : public Base { void myfun() { cout << "myfun() in Derived\n"; } }; int main() { Derived d; Base &b = d; b.myfun(); return 0; } 

也:

 #include <iostream> class Base final { }; class Derived : public Base { }; int main() { Derived d; return 0; }