🦔

virtual修飾子はオーバーライドしたメソッドに付ける必要はない

2022/11/05に公開2

概要

C++にはvirtual修飾子というのがある。
このvirtual修飾子が基底クラスのメソッドに付いていれば仮想関数として扱われ、基底クラスのポインター経由で派生クラスのメソッドを実行することができる。
このvirtual修飾子だが、派生クラスでオーバーライドしたメソッドに対しては付ける必要があるかどうか分からなかったため検証した。

結論

virtual修飾子はオーバーライドしたメソッドに付ける必要はない。

詳細

以下のようにコードを書いて検証した。

class Base
{
public:
    virtual void Run() { std::cout << "Base Run" << std::endl; }
};

class Hoge : public Base
{
public:
    void Run() { std::cout << "Hoge Run" << std::endl; }
};

class Fuga : public Hoge
{
public:
    void Run() { std::cout << "Fuga Run" << std::endl; }
};

int main()
{
    Base* p1 = new Hoge();
    p1->Run(); // "Hoge Run"
    delete p1;

    Hoge* p2 = new Fuga();
    p2->Run(); // "Fuga Run"
    delete p2;

    return 0;
}

Baseクラスにはvirtual修飾子が付いた仮想関数のRunメソッドが定義されている。
そのため、Hoge型のポインタをBase型のポインタに代入しRunメソッドを実行すると、当然HogeクラスのRunメソッドが呼び出される。

では、Fuga型のポインタをHoge型のポインタに代入しRunメソッドを実行した場合はどうか。
HogeクラスのRunメソッドにはvirtual修飾子が付いていないが、FugaクラスのRunメソッドが呼び出された。
どうやら基底のBaseクラスのRunメソッドに付いているので仮想関数として扱ってくれる模様。

overrideしたメソッドにはvirtual修飾子は不要というのが分かった。

Discussion

齊藤敦志齊藤敦志

仕様だとこのあたりに記述されています。

https://timsong-cpp.github.io/cppwp/n3337/class.virtual#2

関数名、引数リスト、CV修飾、参照修飾が基底の仮想メンバ関数と等しい場合には仮想関数になるという規則です。

whether or not it is so declared (そのように宣言されているかどうかと関係なく)

と明記されており、仮想関数として宣言されているかどうかに関係なく問答無用で仮想関数です。

ただ、この仕様は失敗だったと考えられています。 オーバーライドにしているつもりで名前や引数が間違えていてもエラーにならない (単に新しいメンバ関数が宣言されたことになる) ので問題を発見しづらいことがあるからです。 また、プログラマが読むときも継承構造を把握して基底にあるメンバを知っていないとそれがオーバーライドかどうかわからないのは読みづらいです。

そういう理由で override キーワードが C++11 から導入されたという経緯があります。 override キーワードが必須ではないのは互換性を維持するために仕方なくそうなっているだけなので、現在ではオーバーライドするときは常に override で修飾するのが良い作法です。

meloQmeloQ

コメントありがとうございます。
overrideキーワードを付けることでコンパイル時にオーバーライドできていないことに気付けるの良いですね。overrideキーワード活用します。