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 };
维基百科还提出了一个有趣的观点 :
请注意,
override
和final
都不是语言关键字。 他们在技术上是标识符; 在这些特定的环境中使用它们只会获得特殊的意义 。 在任何其他位置,它们可以是有效的标识符。
“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; }