今回も「基底クラス」となるクラスから作っていきましょう。
Monsterと言うクラスを作り、ここから「派生クラス」を作りながら色々と説明します。
少し長くなりそうですから、ファイル分割して作りましょう。
とりあえず、プロジェクトを作り↓の4つのファイルを追加してください。
Main.h Main.cpp Monster.h Monster.cpp
まずはmain関数を作るため、Main.cppを開きます。
<sample program cpp073-01>
/* Main.cpp */
#include "Main.h" int main() { return 0; } |
次にMonster.hを開き、Monsterクラスの宣言を作ります。
<sample program cpp073-02>
/* Monster.h */
#pragma once class Monster { public: Monster(); void Initialize(const int hp, const int mp); void ShowStatus() const; private: int m_hp; int m_mp; }; |
メンバ変数としてHPとMP、メンバ関数として初期化とステータス表示を用意しました。
続いて関数本体を作るため、Monster.cppを開きます。
<sample program cpp073-03>
/* 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; } |
メンバ変数の初期値はコンストラクタ関数ではなく、Initialize関数で行いました。
プログラムの実行中に、何度かメンバ変数の値をリセットする必要があるのであれば、コンストラクタ関数で値を設定しないケースもあります。
コンストラクタ関数は、クラスが実体を持った時に1度しか呼び出されません。
値を初期値に戻すために、クラスの実体を破棄しなければならないのは面倒です。
別の関数にしておけば、いつでも呼び出せますのでリセットするのも簡単です。
※実際はケースバイケースです。
上手く作れているかどうか試すために、動作テストを行います。
Main.hを開いて、Monster.hをインクルードしましょう。
<sample program cpp073-04>
/* Main.h */
#pragma once #include "Monster.h" |
これで、Main.cppでMonsterクラスが使えるようになりました。
では、Main.cppを開いてテスト用のコードを追加しましょう。
<sample program cpp073-05>
/* Main.cpp */
#include "Main.h"
int main()
{
Monster monster;
monster.Initialize(100, 30);
monster.ShowStatus();
return 0;
}
|
<実行結果>
HP = 100 : MP = 30 続行するには何かキーを押してください・・・
ちゃんと実行出来ています。
さて、ここからが本番です。
このMonsterクラスを「基底クラス」として「派生クラス」を作りましょう。
では次のファイルを追加してください。
Dragon.h Dragon.cpp
Monsterクラスから派生したDragonクラスを作ります。
まずは、Dragon.hを開いて、Monsterクラスを継承したDragonクラスを作りましょう。
<sample program cpp073-06>
/* Dragon.h */
#pragma once #include "Monster.h" class Dragon : public Monster { }; |
単純にMonsterクラスを継承しただけのクラスです。
きちんと継承出来ているかどうか試しましょう。
Main.hを開いて、Dragon.hをインクルードします。
<sample program cpp073-07>
/* Main.h */
#pragma once
#include "Dragon.h"
|
Monster.hはDragon.hでインクルードしてあるので書かなくても良いです。
次に、Main.cppを開きテスト用のコードを書きます。
<sample program cpp073-08>
/* Main.cpp */
#include "Main.h"
int main()
{
Dragon dragon;
dragon.Initialize(1000, 500);
dragon.ShowStatus();
return 0;
}
|
<実行結果>
HP = 1000 : MP = 500 続行するには何かキーを押してください・・・
上手くいっているようです。
では、ここで「派生クラス」独自の関数を追加してみます。
ドラゴンなので、ドラゴン独自の関数FireBreathを追加してみます。
FireBreath関数は「火を吐く」メッセージを表示し、MPを10減らします。
Dragon.hを開いてプロトタイプ宣言を書きましょう。
<sample program cpp073-09>
/* Dragon.h */
#pragma once
#include "Monster.h"
class Dragon : public Monster {
public:
void FireBreath();
};
|
次にDragon.cppを開いて、FireBreath関数の本体を書きます。
<sample program cpp073-10>
/* Dragon.cpp */
#include "Dragon.h" #include <iostream> void Dragon::FireBreath() { std::cout << "Fire Breath!!" << std::endl; m_mp -= 10; } |
出来たらビルドしてみてください。
<ビルド結果>
error C2248: 'Monster::m_mp': private メンバー (クラス 'Monster' で宣言されている) にアクセスできません。 note: 'Monster::m_mp' の宣言を確認してください note: 'Monster' の宣言を確認してください
エラーになりました。
エラーメッセージを見ると、「m_mp」がprivateなのでアクセス出来ないと書いてあります。
DragonクラスはMonsterクラスを受け継いでいますので、メンバ変数「m_mp」も受け継いでいるはずです。
ですが、アクセス出来ないのには理由があります。
Monsterクラスのprivateは、継承先でもMonsterクラスのprivateなので、他のクラスからはアクセス出来ません。
たとえ継承された「派生クラス」からもアクセス出来ないのです。
これを解消するのが、表題の「protectedメンバ」です。
クラスのスコープは3種類あります。
private 自身のクラス内でしか使えない public どこからでも使える protected 派生クラスからは使えるが、他は使えない |
今回の場合、「m_hp」や「m_mp」はprotectedメンバにする事で「派生クラス」からも使えそうです。
Monster.hを開きスコープを変更してみましょう。
<sample program cpp073-11>
/* Monster.h */
#pragma once
class Monster {
public:
Monster();
void Initialize(const int hp, const int mp);
void ShowStatus() const;
protected:
int m_hp;
int m_mp;
};
|
ビルドするとエラーは消えました。
Main.cppを開き、FireBreath関数を呼んでみましょう。
<sample program cpp073-12>
/* Main.cpp */
#include "Main.h"
int main()
{
Dragon dragon;
dragon.Initialize(1000, 500);
dragon.ShowStatus();
dragon.FireBreath();
return 0;
}
|
<実行結果>
HP = 1000 : MP = 500 Fire Breath!! 続行するには何かキーを押してください・・・
上手くいきました!
「基底クラス」を作る時には、privateにするのかprotectedにするかよく考えてから作るようにしましょう。