final
C++ 11中关键字用于函数的目的是什么?我理解它可以防止派生类的函数覆盖,但如果是这种情况,那么将你的final
函数声明为非虚函数是不够的呢?还有其他我想念的东西吗?
Dav*_*eas 118
您遗漏的内容,因为在注释中已经提到的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!
};
Run Code Online (Sandbox Code Playgroud)
Naw*_*waz 117
这是为了防止类被继承.来自维基百科:
C++ 11还增加了阻止从类继承或仅防止派生类中的重写方法的能力.这是通过特殊标识符final完成的.例如:
Run Code Online (Sandbox Code Playgroud)struct Base1 final { }; struct Derived1 : Base1 { }; // ill-formed because the class Base1 // has been marked final
它还用于标记虚函数,以防止它在派生类中被覆盖:
Run Code Online (Sandbox Code Playgroud)struct Base2 { virtual void f() final; }; struct Derived2 : Base2 { void f(); // ill-formed because the virtual function Base2::f has // been marked final };
Wikipedia进一步提出了一个有趣的观点:
请注意,既不是语言关键字
override
也不final
是.它们在技术上是标识符; 它们只在特定情况下使用时才有特殊意义.在任何其他位置,它们可以是有效的标识符.
这意味着,允许以下内容:
int const final = 0; // ok
int const override = 1; // ok
Run Code Online (Sandbox Code Playgroud)
小智 41
"final"还允许编译器优化绕过间接调用:
class IAbstract
{
public:
virtual void DoSomething() = 0;
};
class CDerived : public IAbstract
{
void DoSomething() final { m_x = 1 ; }
void Blah( void ) { DoSomething(); }
};
Run Code Online (Sandbox Code Playgroud)
使用"final",编译器可以CDerived::DoSomething()
直接从内部调用Blah()
,甚至可以内联调用.没有它,它必须在内部生成一个间接调用,Blah()
因为Blah()
可以在被覆盖的派生类中调用DoSomething()
.
小智 28
没有什么可以添加到"最终"的语义方面.
但我想补充一下克里斯·格林的评论,即"最终"可能会在不久的将来成为一种非常重要的编译器优化技术.不仅在他提到的简单案例中,而且在更复杂的现实世界的类层次结构中,它们可以被"最终""封闭",从而允许编译器生成比通常的vtable方法更有效的调度代码.
vtable的一个关键缺点是对于任何这样的虚拟对象(假设典型的Intel CPU上为64位),指针单独占用高速缓存行的25%(64字节中的8个).在我喜欢写的应用程序中,这很痛苦.(根据我的经验,从纯粹的性能角度来看,这是反对C++的第一个论点,即C程序员.)
在需要极高性能的应用程序中,这对于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'
Run Code Online (Sandbox Code Playgroud)
能够将非虚拟方法标记为"最终"并不是非常有意义.特定
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
Run Code Online (Sandbox Code Playgroud)
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 */ };
Run Code Online (Sandbox Code Playgroud)
然后final
就可以做多少重写放置一个"底线".其他类可以扩展A和B并覆盖它们foo
,但是一个类扩展了C然后它是不允许的.
因此,制作"顶级"foo final
可能没有意义,但它可能有意义降低.
(我认为,有空间可以扩展最终的词语并覆盖非虚拟成员.但它们会有不同的含义.)
我喜欢的'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 */}
};
Run Code Online (Sandbox Code Playgroud)
final 关键字允许您声明一个虚拟方法,将其覆盖 N 次,然后强制要求“不能再覆盖”。这对于限制派生类的使用很有用,这样您就可以说“我知道我的超类允许您覆盖它,但是如果您想从我那里派生,则不能!”。
struct Foo
{
virtual void DoStuff();
}
struct Bar : public Foo
{
void DoStuff() final;
}
struct Babar : public Bar
{
void DoStuff(); // error!
}
Run Code Online (Sandbox Code Playgroud)
正如其他海报指出的那样,它不能应用于非虚拟功能。
final 关键字的一个目的是防止意外覆盖方法。在我的示例中, DoStuff() 可能是一个辅助函数,派生类只需重命名即可获得正确的行为。没有final,直到测试才会发现错误。
final
添加一个明确的意图是不覆盖您的函数,如果违反此规则,则会导致编译器错误:
struct A {
virtual int foo(); // #1
};
struct B : A {
int foo();
};
Run Code Online (Sandbox Code Playgroud)
如代码所示,它将进行编译和B::foo
覆盖A::foo
。B::foo
顺便说一句,它也是虚拟的。但是,如果将#1更改为virtual int foo() final
,则这是编译器错误,并且不允许我们A::foo
在派生类中进一步覆盖任何内容。
请注意,这不允许我们“重新打开”新的层次结构,即无法创建B::foo
新的,不相关的功能,这些功能可以独立于新的虚拟层次结构的开头。一旦函数是最终函数,就永远不能在任何派生类中再次声明它。
归档时间: |
|
查看次数: |
88764 次 |
最近记录: |