[C++] 継承クラスでの、仮想関数による再定義(オーバーライド)と、普通の関数の再定義(隠ぺい)の違い

Pocket

よくある仮想関数を使ったポリモーフィズム(多様性)の例とは少し違うけど
(複数の種類の派生クラスのアドレスを、親クラスのポインタに代入するやつ)、
メソッドをオーバーライドした場合と、隠ぺいした場合の動作の違いを例示すると、
こんな感じのソースになる。

#include <iostream>
using std::cout;
using std::endl;

class CBase {
public:
void funcCall(char *c) {
    cout << c << func01() << "," << func02() << endl;
}

virtual char *func01() {return " Base01";}
        char *func02() {return " Base02";}
};

class CDerived : public CBase {
public:
char *func01() {return " Derv01";}    // オーバーライド
char *func02() {return " Derv02";}    // 隠ぺい
};

int main() {
    CBase       obj01;
    CDerived    obj02;

    // [1]
    obj01.funcCall("BaseClass:");

    // [2]
    obj02.funcCall("DervClass:");

    // [3]
    cout << "DervClass:" << obj02.func01() << "," << obj02.func02() << endl;

    return 0;
}

obj01 は CBase クラスなので、当然 CBase の func01 と func02 の関数が呼ばれる。([1])

一方の obj02 は、 CDerived クラスだが、 funcCall 関数は CBase クラスのメソッドなので、
func01 と func02 を funcCall 関数から呼ぶと、
オーバーライドされている func01 は CDerived のものが呼ばれ、
単に同じ関数名で隠ぺいしているだけの func02 は、 CBase のものが呼ばれる。([2])

しかし、 obj02 の func01 と func02 を直接呼べば、両方とも CDerived のものが呼ばれる。([3])

従って、出力結果は以下の様になる。
実行結果

つまるところ、 CDerived クラスの func02 関数は、
CDerived という新たなスコープで func02 関数が定義されているだけなので、
CBase というスコープ (funcCall 関数内からの呼び出し) からは CBase の func02 が呼ばれ、
CDerivedというスコープ (obj02 のメソッドとしての呼び出し) からはCDerivedのfunc02が呼ばれると言うわけだ。

ブロックスコープで例えてみるなら、以下のソースの出力が

// 元のスコープ
int a = 8;
{   // ブロックによる入れ子スコープ
    int a = 16; // "元のスコープ"のa変数を隠ぺいする
    cout << a << endl;
}
cout << a << endl;
16
8

 
 となることに疑念はないだろう。

1 thought on “[C++] 継承クラスでの、仮想関数による再定義(オーバーライド)と、普通の関数の再定義(隠ぺい)の違い

  1. ピンバック: Aqua Ware つぶやきブログ » WM_SIZEをハンドルしないCDialogResizeを作る

コメントを残す

メールアドレスが公開されることはありません。

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください