私が理解しているように、override
キーワードは、特定の宣言がベースvirtual
メソッドを実装することを示し、一致するベースメソッドが見つからない場合、コンパイルは失敗するはずです。
final
キーワードの私の理解は、このvirtual
関数をオーバーライドするクラスはないことをコンパイラーに伝えることです。
override final
冗長ですか? うまくコンパイルできるようです 。どんな情報override final
final
がそうではないことを伝える?そのような組み合わせのユースケースは何ですか?
final
は、関数が最初に何かをオーバーライドすることを必要としません。その効果は[class.virtual]/4で次のように定義されています
何らかのクラス
f
の仮想関数B
がvirt-specifierfinal
でマークされている場合クラス内のD
から派生したB
関数D::f
オーバーライドB::f
、プログラムの形式が正しくありません。
それでおしまい。今override final
は単に意味する
„この関数は、基本クラスの1つ(override
)をオーバーライドし、それ自体はオーバーライドできません(final
)。final
自体は、より弱い要件を課します。 override
とfinal
には独立した動作があります。
ただし、final
は仮想関数にのみ使用できることに注意してください-[class.mem]/8
virt-specifier-seqは、仮想メンバー関数(10.3)の宣言でのみ表示されます。
したがって、宣言
void foo() final;
実質的に同じです
virtual void foo() final override;
両方が何かをオーバーライドするためにfoo
を必要とするので-override
を使用することによる2番目の宣言、およびfoo
が暗黙的に仮想である場合にのみ有効な最初の宣言、つまりfoo
が基底クラスのfoo
という仮想関数をオーバーライドする場合、派生クラスのfoo
を自動的に仮想化します。したがってoverride
は、final
ではなくvirtual
が発生する宣言では不要です。
それでも、後者の宣言は意図をより明確に表しており、間違いなく優先されるべきです。
final
は、関数がオーバーライドされることを必ずしも意味しません。仮想関数を継承階層のfirst宣言でfinal
として宣言することは(多少疑わしい場合)完全に有効です。
仮想のすぐに最終的な関数を作成するために考えられる理由の1つは、同じ名前とパラメーターに異なる意味を与えることから派生クラスをpreventにしたい場合です。
(急いでいる場合は、最後までスキップして結論を確認してください。)
override
とfinal
は両方とも、仮想関数の宣言でのみ使用できます。また、両方のキーワードを同じ関数宣言で使用できますが、両方を使用することが有用かどうかは状況によって異なります。
例として次のコードを取り上げます。
_#include <iostream>
using std::cout; using std::endl;
struct B {
virtual void f1() { cout << "B::f1() "; }
virtual void f2() { cout << "B::f2() "; }
virtual void f3() { cout << "B::f3() "; }
virtual void f6() final { cout << "B::f6() "; }
void f7() { cout << "B::f7() "; }
void f8() { cout << "B::f8() "; }
void f9() { cout << "B::f9() "; }
};
struct D : B {
void f1() override { cout << "D::f1() "; }
void f2() final { cout << "D::f2() "; }
void f3() override final { cout << "D::f3() "; } // need not have override
// should have override, otherwise add new virtual function
virtual void f4() final { cout << "D::f4() "; }
//virtual void f5() override final; // Error, no virtual function in base class
//void f6(); // Error, override a final virtual function
void f7() { cout << "D::f7() "; }
virtual void f8() { cout << "D::f8() "; }
//void f9() override; // Error, override a nonvirtual function
};
int main() {
B b; D d;
B *bp = &b, *bd = &d; D *dp = &d;
bp->f1(); bp->f2(); bp->f3(); bp->f6(); bp->f7(); bp->f8(); bp->f9(); cout << endl;
bd->f1(); bd->f2(); bd->f3(); bd->f6(); bd->f7(); bd->f8(); bd->f9(); cout << endl;
dp->f1(); dp->f2(); dp->f3(); dp->f6(); dp->f7(); dp->f8(); dp->f9(); cout << endl;
return 0;
}
_
出力は
_B::f1() B::f2() B::f3() B::f6() B::f7() B::f8() B::f9()
D::f1() D::f2() D::f3() B::f6() B::f7() B::f8() B::f9()
D::f1() D::f2() D::f3() B::f6() D::f7() D::f8() B::f9()
_
f1()
とf6()
を比較します。 override
とfinal
は意味的に独立していることがわかっています。
override
は、関数が基本クラスの仮想関数をオーバーライドしていることを意味します。 f1()
およびf3()
を参照してください。final
は、派生クラスによって関数をオーバーライドできないことを意味します。 (ただし、関数自体は基本クラスの仮想関数をオーバーライドする必要はありません。)f6()
およびf4()
を参照してください。f2()
とf3()
を比較します。メンバー関数がvirtual
なしで、およびfinal
付きで宣言されている場合、それは既に基本クラスの仮想関数をオーバーライドしていることを意味します。この場合、キーWord override
は冗長です。
f4()
とf5()
を比較します。メンバー関数がvirtual
で宣言されていて、それが継承階層のfirst仮想関数ではない場合、 override
は、オーバーライド関係を指定します。そうしないと、派生クラスに誤って新しい仮想関数を追加する可能性があります。
f1()
とf7()
を比較します。仮想関数だけでなく、任意のメンバー関数を派生クラスでオーバーライドできることがわかっています。 virtual
が指定するのはpolymorphismです。これは、実行する関数の決定がコンパイル時ではなく実行時まで遅れることを意味します。 (これは実際には避けるべきです。)
f7()
とf8()
を比較します。基本クラス関数をオーバーライドして、新しい仮想関数にすることさえできることを知っています。 (これは、D
から派生したクラスの任意のメンバー関数f8()
が仮想であることを意味します。)(これも実際には避けるべきです。)
f7()
とf9()
を比較します。 override
は、派生クラスの仮想関数をオーバーライドするときにエラーを見つけるのに役立ちますが、基本クラスにキーワードvirtual
を追加するのを忘れたことがわかります。
結論として、私自身の見解でのベストプラクティスは次のとおりです。
virtual
を使用基本クラスの関数。override
も指定されていない限り、派生クラスで仮想関数のオーバーライドを指定するには、常にfinal
を使用します。いいえfinal
は必ずしもoverride
を意味するわけではありません。実際、virtual
関数を宣言して、すぐにfinal
こちらを参照 と宣言できます。 final
キーワードは、派生class
がこの関数のオーバーライドを作成できないことを単に示しています。
override
キーワードは、(関連のない新しい関数を宣言する代わりに)実際に仮想関数を実際にオーバーライドすることを強制するという点で重要です。 override
に関するこの投稿 を参照してください
要するに、彼らはそれぞれ独自の特定の目的を果たしており、しばしば両方を使用するのが正しいです。
次のコード(final
指定子付き)がコンパイルされます。ただし、final
がoverride final
に置き換えられると、コンパイルは失敗します。したがって、override final
は、単なるfinal
よりも多くの情報を伝達します(コンパイルを防ぎます)。
class Base
{
public:
virtual ~Base() {}
};
class Derived : public Base
{
public:
virtual void foo() final
{
std::cout << "in Derived foo\n";
}
};
基本的に、override final
は、このメソッドを派生クラスでオーバーライドできないことを示しますおよびこのメソッドは、基本クラスの仮想メソッドをオーバーライドします。 final
だけでは、基本クラスのオーバーライド部分は指定されません。