★オーバーライドと仮想関数★


前回のプログラムの続きで、新しい考え方を説明します。

簡単な題材が思いつかなかったので、「説明文(Explanation)の表示」を作ってみようと思います。


新しい関数の追加


まずは、Monsterクラスに「モンスターの説明」を表示するExplanation関数を追加します。

Monster.hを開いて、プロトタイプ宣言を書きましょう。

<sample program cpp074-01>

/* Monster.h */

#pragma once

class Monster {
public:

	Monster();

	void Initialize(const int hp, const int mp);

	void ShowStatus() const;

	void Explanation() const;

protected:

	int m_hp;
	int m_mp;
};

続いて、Monster.cppを開き関数の本体を追加します。

<sample program cpp074-02>

/* Monster.cpp */

#include "Monster.h"

#include <iostream>

Monster::Monster()
{

}

void Monster::Initialize(const int hp, const int mp)
{
    m_hp = hp;
    m_mp = mp;
}

void Monster::ShowStatus() const
{
    std::cout << "HP = " << m_hp << " : MP = " << m_mp << std::endl;
}

void Monster::Explanation() const
{
    std::cout << "畏怖すべき存在を体現した架空の生物。" << std::endl;
}

※説明文は「ウィキペディア」から引用させていただきました。


動作テスト


Monsterクラスに追加した関数は、Dragonクラスに継承されていますので、Main.cppで動かしてみましょう。

<sample program cpp074-03>

/* Main.cpp */

#include "Main.h"

int main()
{
    Dragon dragon;

    dragon.Initialize(1000, 500);

    dragon.ShowStatus();

    dragon.Explanation();

    return 0;
}

<実行結果>

HP = 1000 : MP = 500
畏怖すべき存在を体現した架空の生物。
続行するには何かキーを押してください・・・

上手く「説明文」が表示されました。

しかし、あくまでも「モンスター全般の説明」であり、ドラゴンの説明としては不十分ですよね。

やはり、ドラゴン独自の説明文が欲しいところです。

「派生クラス」は独自の関数が作れますので、ExplainDragon関数を作る事も出来るのですが、継承には新しい機能があるのです。


オーバーライド


「派生クラス」では、継承元のクラスの関数と引数、戻り値が全く同じ関数を作ることが出来ます。

これを「オーバーライド(override)」と言い、「無効にする」とか「優先する」という意味になります。

「基底クラス」で宣言されている関数と全く同じ関数を宣言すると「オーバーライド」された事になります。

実際にやってみましょう。

Dragon.hを開いて、Monsterクラスと全く同じExplanation関数を宣言します。

<sample program cpp074-04>

/* Dragon.h */

#pragma once

#include "Monster.h"

class Dragon : public Monster {
public:

    void FireBreath();

    void Explanation() const;
};

続いてDragon.cppを開き、関数本体を書きます。

<sample program cpp074-05>

/* Dragon.cpp */

#include "Dragon.h"

#include <iostream>

void Dragon::FireBreath()
{
    std::cout << "ファイアブレス!" << std::endl;

    m_mp -= 10;
}

void Dragon::Explanation() const
{
    std::cout << "ヨーロッパの文化で共有されている伝承や神話における伝説上の生物。"
        << std::endl << "その姿はトカゲあるいはヘビに似ている。" << std::endl;
}

※説明文は「ウィキペディア」から引用させていただきました。

Monsterクラスと全く同じ、「戻り値」「関数名」「引数」の関数を追加しました。

ただし、中身の説明文は異なっています。


そのままビルドして実行してください。

<実行結果>

HP = 1000 : MP = 500
ヨーロッパの文化で共有されている伝承や神話における伝説上の生物。
その姿はトカゲあるいはヘビに似ている。
続行するには何かキーを押してください・・・

さっきまで、Monsterクラスの説明文が表示されていましたが、ドラゴンの説明文に差し替わりました。

Explanation関数はMonsterクラスとDragonクラスに存在していますが、Dragonクラスの実体を作っているのでDragonクラスのExplanation関数が動作しています。


基底クラスの関数を呼び出す


「基底クラス」の関数を「無効にする」とか「優先する」、と書きましたが「基底クラス」の関数もちゃんと残っています。

例えば、Dragon.cppを次のように変更して実行してみてください。

<sample program cpp074-06>

/* Dragon.cpp */

#include "Dragon.h"

#include <iostream>

void Dragon::FireBreath()
{
    std::cout << "ファイアブレス!" << std::endl;

    m_mp -= 10;
}

void Dragon::Explanation() const
{
    Monster::Explanation();

    std::cout << "ヨーロッパの文化で共有されている伝承や神話における伝説上の生物。"
        << std::endl << "その姿はトカゲあるいはヘビに似ている。" << std::endl;
}
HP = 1000 : MP = 500
畏怖すべき存在を体現した架空の生物。
ヨーロッパの文化で共有されている伝承や神話における伝説上の生物。
その姿はトカゲあるいはヘビに似ている。
続行するには何かキーを押してください・・・

モンスターの説明文が追加されているのが分かります。

「基底クラス」の関数を呼び出すために↓のように書いています。

Monster::Explanation();

関数名が同じですから区別するために「所属」を書きます。


仮想関数


オーバーライドされた関数の「基底クラス」のプロトタイプ宣言の前に「virtual」キーワードを付けることで「仮想関数」になります。

「仮想関数」は上手く使えば非常に強力な武器となる技術ですのでしっかり理解してください。

Monster.hを開いてExplanation関数の前に「virtual」キーワードを付けてください。

<sample program cpp074-07>

/* Monster.h */

#pragma once

class Monster {
public:

    Monster();

    void Initialize(const int hp, const int mp);

    void ShowStatus() const;

    virtual void Explanation() const;

protected:

    int m_hp;
    int m_mp;
};

「virtual」を付ける事によって、この関数は「仮想関数」になりました。


仮想関数の特徴は「基底クラス」の参照やポインタを使った時に現れます。

最初は参照やポインタを使わず、「派生クラス」を「基底クラス」にコピーしてみます。

Main.cppを開き、main関数を書き換えて試してみましょう。

<sample program cpp074-08>

/* Main.cpp */

#include "Main.h"

int main()
{
    Dragon dragon;

    dragon.Initialize(1000, 500);

    Monster monster = dragon;

    monster.ShowStatus();

    monster.Explanation();

    return 0;
}

<実行結果>

HP = 1000 : MP = 500
畏怖すべき存在を体現した架空の生物。
続行するには何かキーを押してください・・・

Dragonクラスを作り、「基底クラス」であるMonsterクラスに代入しました。

「ShowStatus関数」「Explanation関数」はMonsterクラスにもありますから呼び出す事が出来ますが、「FireBreath関数」はDragonクラス独自の関数なので呼ぶ事は出来ません。

「Explanation関数」もMonsterクラスの説明文が表示されています。

Monsterクラスになっているのだから当然の結果と言えます。

では、これを次のように変更して実行してみてください。

<sample program cpp074-09>

/* Main.cpp */

#include "Main.h"

int main()
{
    Dragon dragon;

    dragon.Initialize(1000, 500);

    Monster &refMonster = dragon;

    refMonster.ShowStatus();

    refMonster.Explanation();

    return 0;
}

<実行結果>

HP = 1000 : MP = 500
畏怖すべき存在を体現した架空の生物。
ヨーロッパの文化で共有されている伝承や神話における伝説上の生物。
その姿はトカゲあるいはヘビに似ている。
続行するには何かキーを押してください・・・

Monsterクラスの参照を用意し、Dragonクラスを代入しました。

見て欲しいのは説明文です。

Monsterクラスの参照なのに、Dragonクラスの説明文が表示されています。

もちろんポインタでも同じです。

<sample program cpp074-10>

/* Main.cpp */

#include "Main.h"

int main()
{
    Dragon dragon;

    dragon.Initialize(1000, 500);

    Monster *pMonster = &dragon;

    pMonster->ShowStatus();

    pMonster->Explanation();

    return 0;
}

<実行結果>

HP = 1000 : MP = 500
畏怖すべき存在を体現した架空の生物。
ヨーロッパの文化で共有されている伝承や神話における伝説上の生物。
その姿はトカゲあるいはヘビに似ている。
続行するには何かキーを押してください・・・

このように「仮想関数」にすると「基底クラス」の参照やポインタから「派生クラス」の関数を呼び出せるようになります。

これだけでは、何に使うのか分かりませんが、次はこの考え方を使ったプログラムを作ってみましょう。


次へ

戻る

目次へ