9章 オーバーライド

http://www.geocities.jp/ky_webid/cpp/language/009.html

前回継承というものを学びました。

今回このオーバーライドというものなのですが、継承したサブクラスでスーパークラスと同名のメンバ関数を定義し、上書きできるというもののようです。

とにかくまず実装してみましょう。

#include <iostream>

class CBase {
public:
    virtual void foo ();
};

class CSub : public CBase {
public:
    void foo ();
};

void CBase::foo () {
    std::cout << "CBase::foo" << std::endl;
}

void CSub::foo () {
    std::cout << "CSub::foo" << std::endl;
}

int main () {
    CBase objb;
    CSub objs;
    
    objb.foo();
    objs.foo();
    
    return 0;
}
$ main
CBase::foo
CSub::foo

できました。なるほどです。

これやはりvirtualはつけないと動かないですよね、と思ってためしにvirtualなしでやってみても動きました!えええ。

そこで色々調べてみたところ、どういうことか分かりました。

C++の基礎 : 仮想関数

なるほど。

少しまとめます。C++は基本的にvirtualつけようかつけまいが、メンバ関数の上書きそのものはできます。

ではvirtual有り無しで何が違うのかというと、そのメンバ関数を呼び出している別のメンバ関数があった場合に挙動が変わります。

実際に実装を見てみましょう。まずはvirtual無しの場合です。

#include <iostream>

class CBase {
public:
    void foo ();
    void call ();
};

class CSub : public CBase {
public:
    void foo ();
};

void CBase::foo () {
    std::cout << "CBase::foo" << std::endl;
}

void CBase::call () {
    foo();
}

void CSub::foo () {
    std::cout << "CSub::foo" << std::endl;
}

int main () {
    CBase objb;
    CSub objs;
    
    objb.call();
    objs.call();
    
    return 0;
}
$ main
CBase::foo
CBase::foo

スーパークラスCBaseにcallというメンバ関数を持たせてfoo関数を呼び出す処理をしています。

このcall関数をサブクラスCSubから呼び出してもCSubのfooじゃなくてCBaseのfooが呼び出されていますね。

あくまでfoo関数を上書きしただけなのでcallで呼び出しているfooは常にCBaseのfooを見るということになります。

そしてvirtualを使えばこの挙動を変えることができるというわけです。

class CBase {
public:
    virtual void foo (); // virtualを付加
    void call ();
};
$ main
CBase::foo
CSub::foo

変わりましたね。

virtualをつけたfoo関数を呼び出しているcall関数がサブクラスCSubから呼び出された場合、そのCSubで定義されているfoo関数を見るようになるということです。

この機能のことをオーバーライドというみたいです。virtualをつけない関数の再定義は単なる関数の上書きということらしいです。