web-dev-qa-db-ja.com

finalはオーバーライドを意味しますか?

私が理解しているように、overrideキーワードは、特定の宣言がベースvirtualメソッドを実装することを示し、一致するベースメソッドが見つからない場合、コンパイルは失敗するはずです。

finalキーワードの私の理解は、このvirtual関数をオーバーライドするクラスはないことをコンパイラーに伝えることです。

override final冗長ですか? うまくコンパイルできるようです 。どんな情報override finalfinalがそうではないことを伝える?そのような組み合わせのユースケースは何ですか?

46
quant

finalは、関数が最初に何かをオーバーライドすることを必要としません。その効果は[class.virtual]/4で次のように定義されています

何らかのクラスfの仮想関数Bvirt-specifierfinalでマークされている場合クラス内のDから派生したB関数D::fオーバーライドB::f、プログラムの形式が正しくありません。

それでおしまい。今override finalは単に意味する
„この関数は、基本クラスの1つ(override)をオーバーライドし、それ自体はオーバーライドできません(final)。
final自体は、より弱い要件を課します。 overridefinalには独立した動作があります。


ただし、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が発生する宣言では不要です。
それでも、後者の宣言は意図をより明確に表しており、間違いなく優先されるべきです。

40
Columbo

finalは、関数がオーバーライドされることを必ずしも意味しません。仮想関数を継承階層のfirst宣言でfinalとして宣言することは(多少疑わしい場合)完全に有効です。

仮想のすぐに最終的な関数を作成するために考えられる理由の1つは、同じ名前とパラメーターに異なる意味を与えることから派生クラスをpreventにしたい場合です。

16
Angew

(急いでいる場合は、最後までスキップして結論を​​確認してください。)

overridefinalは両方とも、仮想関数の宣言でのみ使用できます。また、両方のキーワードを同じ関数宣言で使用できますが、両方を使用することが有用かどうかは状況によって異なります。

例として次のコードを取り上げます。

_#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()
_
  1. f1()f6()を比較します。 overridefinalは意味的に独立していることがわかっています。

    • overrideは、関数が基本クラスの仮想関数をオーバーライドしていることを意味します。 f1()およびf3()を参照してください。
    • finalは、派生クラスによって関数をオーバーライドできないことを意味します。 (ただし、関数自体は基本クラスの仮想関数をオーバーライドする必要はありません。)f6()およびf4()を参照してください。
  2. f2()f3()を比較します。メンバー関数がvirtualなしで、およびfinal付きで宣言されている場合、それは既に基本クラスの仮想関数をオーバーライドしていることを意味します。この場合、キーWord overrideは冗長です。

  3. f4()f5()を比較します。メンバー関数がvirtualで宣言されていて、それが継承階層のfirst仮想関数ではない場合、 overrideは、オーバーライド関係を指定します。そうしないと、派生クラスに誤って新しい仮想関数を追加する可能性があります。

  4. f1()f7()を比較します。仮想関数だけでなく、任意のメンバー関数を派生クラスでオーバーライドできることがわかっています。 virtualが指定するのはpolymorphismです。これは、実行する関数の決定がコンパイル時ではなく実行時まで遅れることを意味します。 (これは実際には避けるべきです。)

  5. f7()f8()を比較します。基本クラス関数をオーバーライドして、新しい仮想関数にすることさえできることを知っています。 (これは、Dから派生したクラスの任意のメンバー関数f8()が仮想であることを意味します。)(これも実際には避けるべきです。)

  6. f7()f9()を比較します。 overrideは、派生クラスの仮想関数をオーバーライドするときにエラーを見つけるのに役立ちますが、基本クラスにキーワードvirtualを追加するのを忘れたことがわかります。

結論として、私自身の見解でのベストプラクティスは次のとおりです。

  • onlyfirstvirtualの宣言でvirtualを使用基本クラスの関数。
  • overrideも指定されていない限り、派生クラスで仮想関数のオーバーライドを指定するには、常にfinalを使用します。
5
Jaege

いいえfinalは必ずしもoverrideを意味するわけではありません。実際、virtual関数を宣言して、すぐにfinalこちらを参照 と宣言できます。 finalキーワードは、派生classがこの関数のオーバーライドを作成できないことを単に示しています。

overrideキーワードは、(関連のない新しい関数を宣言する代わりに)実際に仮想関数を実際にオーバーライドすることを強制するという点で重要です。 overrideに関するこの投稿 を参照してください

要するに、彼らはそれぞれ独自の特定の目的を果たしており、しばしば両方を使用するのが正しいです。

1
CoryKramer

次のコード(final指定子付き)がコンパイルされます。ただし、finaloverride 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だけでは、基本クラスのオーバーライド部分は指定されません。

1
tcb