62

ウィキペディアには、C++11 の final 修飾子に関する次の例があります。

struct Base2 {
    virtual void f() final;
};

struct Derived2 : Base2 {
    void f(); // ill-formed because the virtual function Base2::f has been marked final
};

仮想関数を導入してすぐに最終的なものとしてマークするポイントがわかりません。これは単なる悪い例ですか、それとも他にもありますか?

4

10 に答える 10

68

通常final、基本クラスの仮想関数の定義では使用されません。final派生型がさらに関数をオーバーライドするのを防ぐために、関数をオーバーライドする派生クラスによって使用されます。オーバーライド関数は通常仮想でなければならないため、誰でもその関数をさらに派生した型でオーバーライドできることを意味します。final別の関数をオーバーライドするが、それ自体はオーバーライドできない関数を指定できます。

たとえば、クラス階層を設計していて関数をオーバーライドする必要があるが、クラス階層のユーザーに同じことをさせたくない場合は、派生クラスで関数を final としてマークすることができます。

于 2012-07-28T21:16:21.677 に答える
11

関数にラベルを付けるには、つまり、C++11§10.3パラでfinalある必要があります。virtual2:

[...]便宜上、仮想関数はそれ自体をオーバーライドすると言います。

およびパラ4:

あるクラスBの仮想関数fがvirt-specifierfi​​nalでマークされ、Bから派生したクラスDで関数D::fがB::fをオーバーライドする場合、プログラムの形式は正しくありません。[...]

つまり、final仮想関数(または継承をブロックするクラス)でのみ使用する必要があります。したがって、この例virtualを有効なC ++コードにするには、この例を使用する必要があります。

編集:完全に明確にするために:「ポイント」は、仮想が使用される理由についての懸念について尋ねました。それが使用される最終的な理由は、(i)コードが他の方法ではコンパイルされないため、および(ii)十分な場合に、より多くのクラスを使用して例をより複雑にする理由です。したがって、仮想最終関数を持つ1つのクラスだけが例として使用されます。

于 2012-07-28T21:06:35.983 に答える
10

私にはまったく役に立たないようです。これは、構文を示すための単なる例だと思います。

考えられる用途の 1 つは、f を実際にはオーバーライド可能にしたくないが、それでも vtable を生成したいが、それでも恐ろしい方法である場合です。

于 2012-07-28T20:30:43.373 に答える
6

仮想関数を導入してすぐに最終的なものとしてマークするポイントがわかりません。

この例の目的は、どのように機能するかを説明するfinalことであり、まさにそれを行っています。

実際の目的は、vtable がクラスのサイズにどのように影響するかを確認することです。

struct Base2 {
    virtual void f() final;
};
struct Base1 {
};

assert(sizeof(Base2) != sizeof(Base1)); //probably

Base2f()プラットフォームの仕様をテストするために単純に使用できます。テスト目的のためだけにあるため、オーバーライドしても意味がないため、 とマークされていfinalます。もちろん、これを行っている場合は、設計に問題があります。virtual個人的には、のサイズを確認するためだけに関数を持つクラスを作成しませんvfptr

于 2012-07-28T20:42:54.330 に答える
5

上記の素晴らしい回答に追加する - これはよく知られている final のアプリケーションです (Java から非常にインスピレーションを得ています)。Base クラスで関数 wait() を定義し、そのすべての子孫で wait() の実装を1 つだけにしたいとします。この場合、wait() を final として宣言できます。

例えば:

class Base { 
   public: 
       virtual void wait() final { cout << "I m inside Base::wait()" << endl; }
       void wait_non_final() { cout << "I m inside Base::wait_non_final()" << endl; }
}; 

派生クラスの定義は次のとおりです。

class Derived : public Base {
      public: 
        // assume programmer had no idea there is a function Base::wait() 

        // error: wait is final
        void wait() { cout << "I am inside Derived::wait() \n"; } 
        // that's ok    
        void wait_non_final() { cout << "I am inside Derived::wait_non_final(); }

} 

wait() が純粋な仮想関数である場合、それは役に立たない(そして正しくない)でしょう。この場合: コンパイラは、派生クラス内で wait() を定義するように求めます。これを行うと、wait() が final であるため、エラーが発生します。

最終関数を仮想にする必要があるのはなぜですか? (これも紛らわしいです)(imo)1)最終的な概念は仮想関数の概念に非常に近いため[仮想関数には多くの実装があります-最終的な関数には実装が1つしかありません]、2)最終的な効果を実装するのは簡単ですvtables を使用しています。

于 2014-03-28T23:23:47.303 に答える
1

これの代わりに:

public:
    virtual void f();

これを書くと便利だと思います:

public:
    virtual void f() final
        {
        do_f(); // breakpoint here
        }
protected:
    virtual void do_f();

主な理由は、潜在的に多くのオーバーライドされた実装のいずれかにディスパッチする前に、ブレークポイントを設定する場所が 1 か所になったためです。悲しいことに(IMHO)、「最終」と言うには、「仮想」と言う必要もあります。

于 2016-09-27T20:04:21.693 に答える