[CPP] overrideしたいメンバー関数にvirtualを付け忘れるとどうなるか?

派生クラスで override することができるのは、基底クラスで virtual をつけたメンバー関数(=仮想関数)だけです。しかし、もし virtualをつけていない(つけるのを忘れた)メンバー関数(=非仮想関数)を派生クラスで override しようと試みると何が起こるでしょうか?

1. virtual修飾子とoverride指定を適切につけたケース

virtual修飾子とoverride指定を適切に記述して、作法通りに仮想関数とoverrideを定義しています。
基底クラス Base のポインタ経由でアクセスしても適切に派生クラスのメンバー関数が呼び出されています。

 

2. virtual修飾子を忘れたケース

もし下記のように基底クラスのメンバー関数にvirtualを付け忘れるとコンパイルエラーが発生します。

 

3. virtual修飾子もoverride指定も忘れたケース

上記のように virtual と override の両方の記述を忘れるとコンパイラーはエラーを発しません。ところが実際に実行してみるとプログラマの意に反して(?)、Baseクラスにアップキャストしたポインタを経由してアクセスすると基底クラスのメンバー関数が呼び出されてしまいます。さて何が起こっている(出来上がっている)のでしょうか???

 

大切なことはスコット・メイヤーズ著『Effective C++ 第3版』に書かれていました。 😀

33項 継承した名前を隠蔽しないようにしよう

 

ここで「名前の隠蔽」と「再定義」について説明します。

 

4. ローカル変数によるグローバル変数の名前の隠蔽

メイン関数の中でローカル変数 x を再定義したことでグローバル変数 x が隠されています。
C言語ではグローバル変数にアクセスする手立てはありませんが、C++ではスコープ解決演算子 :: を使うことで、main関数の中からグローバル変数にアクセスすることができます。

上記のケースではグローバル変数とローカル変数で x という名前の変数が各々一つずつ存在しています。

 

5. 基底クラスのメンバー変数を派生クラスで再定義

上記のコードでは基底クラスで定義されたメンバー変数 x を派生クラスで再定義しています。基底クラスでは整数型(int)で定義した変数を派生クラスでは倍精度浮動小数点数(double)で敢えて再定義しています。再定義ですから基底クラスの型には関係なく、派生クラスでは異なる型で定義できます。
上記のコードでもスコープ解決演算子を使って派生クラスから基底クラスのメンバー変数にアクセスしています。基底クラスのメンバー変数 x と 派生クラスのメンバー変数 x は名前が同じだけで異なる実体を持つことがわかると思います。

 

最初の問いへの答えです。
「overrideしたいメンバー関数にvirtualを付け忘れるとどうなるか?」
継承したメンバー関数の名前を隠蔽して、あらたなメンバー関数を再定義します。overrideではなく再定義であるため、派生クラス(のポインタ)を介して呼び出すと派生クラスで再定義されたメンバー関数が呼び出され、アップキャストしたオブジェクトのポインタを介して呼び出しを試みると、基底クラスのメンバー関数が呼び出されます。