什么是目的final
在C ++ 11的功能关键字? 我的理解是阻止功能压倒一切的派生类,但如果是这样的话,那么是不是就足够了申报非虚你的final
功能是什么? 有我在这里失踪的另一件事?
Answer 1:
你所缺少的,如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!
};
Answer 2:
这是为了防止一个类被继承。 从维基百科 :
C ++ 11还增加了防止类继承或简单地防止在派生类覆盖方法的能力。 这是一个特殊的标识符最终完成。 例如:
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
是语言的关键字。 他们在技术上标识; 在这些特定情况下使用时,他们只获得特殊的意义 。 在任何其他位置,它们可以是有效的标识符。
这意味着,以下是允许的:
int const final = 0; // ok
int const override = 1; // ok
Answer 3:
“最终”还允许编译器优化绕过间接调用:
class IAbstract
{
public:
virtual void DoSomething() = 0;
};
class CDerived : public IAbstract
{
void DoSomething() final { m_x = 1 ; }
void Blah( void ) { DoSomething(); }
};
与“最终”,编译器可以调用CDerived::DoSomething()
从内部直接Blah()
甚至在线。 没有它,它有可能产生的内部间接调用Blah()
因为Blah()
可以被称为已重写派生类中DoSomething()
Answer 4:
什么要补充的“最后”的语义方面。
但我想补充克里斯绿色的意见,即“最终”有可能成为不那么遥远的未来的一个非常重要的编译器优化技术 。 不仅在他所提到的简单的情况下,也为更复杂的现实世界中的类层次结构可以是由“最终”,从而使编译器比一般的虚函数表的方法生成更高效的调度代码“关闭”。
的vtables的一个主要缺点是,任何这样的虚拟对象(典型的Intel的CPU上假设64比特)的指针单独吃掉的高速缓存线的25%(64 8个字节)。 在那种我喜欢编写应用程序,这会伤害得很厉害。 (从我的经验,它是针对从一个纯粹的性能上来看,通过C语言编程,即C ++的#1参数)。
在需要极高的性能,这是不那么寻常的C ++应用程序,这可能确实成为真棒,不需要在C风格的或怪异的模板杂耍手动解决此问题。
这种技术被称为Devirtualization。 一个术语值得记住。 :-)
有由安德烈Alexandrescu的一个伟大的最近的一次讲话这很好解释了如何解决办法这种情况今天,如何“最后”可能在未来解决类似案件“自动”(与听众讨论)的一部分:
http://channel9.msdn.com/Events/GoingNative/2013/Writing-Quick-Code-in-Cpp-Quickly
Answer 5:
最终不能应用于非虚函数。
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的将其覆盖。 这可能是不可取的,因此它将使意义使虚拟功能决赛。
现在的问题是,虽然, 为什么还允许最终的虚函数。 如果你有一个深层次:
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
把多少压倒一切可以做一个“地板”。 其它类可以扩展A和B以及覆盖其foo
,但它一类扩展C,那么它是不允许的。
因此,它可能没有意义,使“顶级”富final
,但它可能是有意义的低了下去。
(我觉得虽然有空间扩大的话最终并覆盖到非虚拟的成员。他们会具有不同的意义,但。)
Answer 6:
用例为我喜欢的是如下的“最终”关键字:
// 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 */}
};
Answer 7:
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
一个新的,不相关的功能,可以在一个新的虚拟层级的头是独立。 一旦一个功能是最终的,它永远不能再在任何派生类中声明。
Answer 8:
最终的关键字可以声明一个虚方法,覆盖了N次,然后强制“这不能被重写”。 这将是在限制使用您的派生类的有用的,所以你可以说“我知道我的超级类,您可以覆盖这一点,但如果你想从我的派生,你不能!”。
struct Foo
{
virtual void DoStuff();
}
struct Bar : public Foo
{
void DoStuff() final;
}
struct Babar : public Bar
{
void DoStuff(); // error!
}
作为其他的海报指出的,它不能被应用到非虚拟功能。
最后关键字的一个目的是为了防止方法的意外覆盖。 在我的例子,DoStuff()可能是一个辅助函数派生类只需要重命名以获得正确的行为。 如果没有最后的,也不会被发现的错误,直到测试。
Answer 9:
当添加到函数在C ++最终关键字,防止它通过一个基类被重写。 另外,当加入到类防止任何类型的继承。 请看下面的例子展示使用最终说明符。 该程序在编译失败。
#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;
}
Answer 10:
补充马里奥Knezović的回答是:
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();
上面的代码显示了理论,但实际上没有真实的编译器测试。 如果有人粘贴拆卸输出大加赞赏。