final
C ++ 11の関数のキーワードの目的は何ですか?派生クラスによる関数のオーバーライドを防ぐことは理解していますが、これが当てはまる場合は、final
関数を非仮想として宣言するだけで十分ではないでしょうか?私がここで見逃している別のものはありますか?
final
C ++ 11の関数のキーワードの目的は何ですか?派生クラスによる関数のオーバーライドを防ぐことは理解していますが、これが当てはまる場合は、final
関数を非仮想として宣言するだけで十分ではないでしょうか?私がここで見逃している別のものはありますか?
回答:
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アプローチよりも効率的なディスパッチコードを生成できます。
vtableの主な欠点の1つは、そのような仮想オブジェクト(通常の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はそれをオーバーライドします。これは望ましくない場合があるため、仮想関数をfinalにすることは理にかなっています。
しかし問題は、なぜ仮想関数でfinalを許可するかです。深い階層がある場合:
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を拡張する場合は許可されません。
そのため、「トップレベル」のfooを作成することはおそらく意味final
がありませんが、それよりも下に置くことは意味があります。
(ただし、finalとoverrideという単語を非仮想メンバーに拡張する余地はあると思います。ただし、意味は異なる場合があります。)
final
。たとえば、すべてShape
のを必要なことがわかっている場合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キーワードの1つの目的は、メソッドの誤ったオーバーライドを防ぐことです。私の例では、DoStuff()がヘルパー関数であった可能性があり、派生クラスは、正しい動作を得るために名前を変更する必要があるだけです。finalがないと、テストするまでエラーは発見されません。
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;
}
マリオ・クネゾビッチの回答の補足:
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
キーワードをか。