什么是目的final
在C ++ 11的功能关键字?我知道它可以防止派生类重写函数,但是如果是这种情况,那么将final
函数声明为非虚函数还不够吗?我在这里还想念另一件事吗?
什么是目的final
在C ++ 11的功能关键字?我知道它可以防止派生类重写函数,但是如果是这种情况,那么将final
函数声明为非虚函数还不够吗?我在这里还想念另一件事吗?
Answers:
正如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!
};
virtual
可能会导致错误,和C ++ 11添加override
标签的功能,将检测情况,无法编译时是指功能覆盖实际皮
这是为了防止类被继承。从维基百科:
C ++ 11还增加了阻止从类继承或仅阻止派生类中的重写方法的功能。这是通过特殊标识符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
是。它们在技术上是标识符;只有在特定的上下文中使用它们时,它们才具有特殊的意义。在任何其他位置,它们可以是有效的标识符。
这就是说,允许以下内容:
int const final = 0; // ok
int const override = 1; // ok
“最终”还允许编译器优化绕过间接调用:
class IAbstract
{
public:
virtual void DoSomething() = 0;
};
class CDerived : public IAbstract
{
void DoSomething() final { m_x = 1 ; }
void Blah( void ) { DoSomething(); }
};
使用“ final”,编译器可以CDerived::DoSomething()
直接从内部调用Blah()
,甚至可以内联。没有它,它必须在内部生成一个间接调用,Blah()
因为Blah()
可以在已重写的派生类中调用它DoSomething()
。
没有什么可以添加到“最终”的语义方面。
但是我想补充一下克里斯·格林的评论,即“最终” 在不久的将来可能会成为非常重要的编译器优化技术。他不仅提到了简单的情况,而且还提到了可以通过“ final”“关闭”的更复杂的现实世界类层次结构,从而使编译器可以比通常的vtable方法生成更有效的调度代码。
vtables的一个主要缺点是,对于任何这样的虚拟对象(假设在典型的Intel CPU上为64位),仅指针本身就占用了缓存行的25%(64个字节中的8个)。在我喜欢编写的应用程序中,这非常痛苦。(从我的经验来看,从纯粹的性能角度(即C程序员),它是反对C ++的#1论点。)
在要求极端性能的应用程序中(对于C ++而言并不罕见),这的确可能变得非常棒,不需要手动以C样式或怪异的模板处理解决此问题。
这种技术称为去虚拟化。一个值得记住的术语。:-)
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将覆盖它。这可能是不希望的,因此使虚拟函数最终化是有意义的。
问题是,为什么要允许最终使用虚拟功能。如果您的层次结构很深:
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则不允许这样做。
因此,使“ top-level” foo final
变得没有意义,但从下往下有意义。
(尽管如此,我仍然可以将final和override扩展到非虚拟成员。但是,它们会有不同的含义。)
final
。例如,如果您知道要所有Shape
s- foo()
某种预定义和确定的值,则任何派生形状都不应修改。还是我错了,在这种情况下可以采用更好的模式吗?编辑:哦,也许是因为在这种情况下,不应该让顶层foo()
virtual
成为开始?但尽管如此,它可以被隐藏,即使正确调用(多态)通过Shape*
...
我喜欢的'final'关键字的用例如下:
// 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
新的,不相关的功能,这些功能可以独立于新的虚拟层次结构的开头。一旦函数是最终函数,就永远不能在任何派生类中再次声明它。
final关键字允许您声明一个虚拟方法,将其重写N次,然后强制执行“不能再重写”。这在限制使用派生类时很有用,这样您可以说:“我知道我的超类允许您重写此类,但是如果您想从我派生,则不能!”。
struct Foo
{
virtual void DoStuff();
}
struct Bar : public Foo
{
void DoStuff() final;
}
struct Babar : public Bar
{
void DoStuff(); // error!
}
正如其他张贴者指出的那样,它不能应用于非虚拟功能。
final关键字的一个目的是防止方法的意外覆盖。在我的示例中,DoStuff()可能是一个辅助函数,派生类只需重命名即可获得正确的行为。没有final,直到测试才能发现错误。
将C ++中的final关键字添加到函数后,可以防止基类覆盖它。另外,当添加到类中时,可以防止任何类型的继承。考虑以下示例,该示例显示了最终说明符的用法。该程序编译失败。
#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;
}
马里奥·克涅佐维奇的答案的补充:
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();
上面的代码显示了理论,但并未在实际的编译器上进行实际测试。如果有人粘贴分解后的输出,将不胜感激。
virtual
关键字,覆盖函数都是隐式虚拟的。