中原です。
なぜか毎週月曜と木曜に講座を書いてますね…
まずは、前回の答えから。
どうでしょう?皆さんできましたでしょうか?
< ID = 26 > コンストラクタが通りました
< ID = 21 > コンストラクタが通りました
< ID = 23 > コンストラクタが通りました
< ID = 23 > デストラクタが通りました
< ID = 30 > コンストラクタが通りました
< ID = 21 > デストラクタが通りました
< ID = 30 > デストラクタが通りました
です。
『ID = 26 のデストラクタが通りました』 は、main 関数が終わるときに通りますので、getch() の部分では、まだ通らないのです。
プログラムに沿って説明しますと、
main()
{
COniichan* Tsuzi;
COniichan Nakahara( 26 ); //コンストラクタが通る
COniichan* Tawa;
Tawa = new COniichan( 21 ); //コンストラクタが通る
Tsuzi = new COniichan( 23 ); //コンストラクタが通る
COniichan* Kamata;
delete Tsuzi; //デストラクタが通る
Kamata = new COniichan( 30 ); //コンストラクタが通る
delete Tawa; //デストラクタが通る
delete Kamata; //デストラクタが通る
getch();
} //ID = 26 のデストラクタが通る
となるのですね。
さて、本日の講座は『継承』についてです。
第4回 C++講座 『継承』
[0回]
C++言語には、3本柱と言って、
『カプセル化』、 『継承』、 『ポリモフィズム』
の3つの大きな書き方が存在します。
これらは、決して
C++言語で必須のことではないのですが、オブジェクト指向でプログラミングをする上で、なくてはならないものだと思ってください。
と、いうことは…、
我々ゲームプログラマーにとって、必ず手に入れなければならないものなんですね!
『カプセル化』については、以前第2回のアクセス指定子のときにお話ししました。
今回は『継承』についてです。
では、早速いってみましょう。
シューテイングで例を示したいと思います。
シューティングには、敵と、味方がいますよね。
そのオブジェクトは必ず、X座標、Y座標を持っているはずです。
また、プレイヤーは弾を撃ち、敵は左に進むだけとします。
HPも持っており、無くなったら死ぬものとします。
もし、今までどおりのやり方で、C++言語で書いたとすると…
//自機クラス=========================
class CPlayer
{
private:
int m_iHp;
float m_fx;
float m_fy;
bool m_bExist;
public:
CPlayer(float fx, float fy) : m_fx( fx ), m_fy( fy ), m_iHp( 100 ), m_bExist( true ){}
~CPlayer(){}
void PlayerMove
{
//自機の動き
}
void ShotBullet()
{
//弾を撃つ処理
}
//死んだかどうか
bool IsDeath()
{
if(m_iHP <= 0){
m_bExist = false;
}
return m_bExist;
}
}:
//敵クラス============================
class CEnemy
{
private;
int m_iHp
float m_fx;
float m_fy;
m_bExist;
public:
CEnemy(float fx, float fy) : m_fx( fx ), m_fy( fy ), m_iHp( 10 ), m_bExist( true ){}0
~CEnemy(){};
void EnemyMove()
{
//敵の動き
}
//死んだかどうか
bool IsDeath()
{
if(m_iHP <= 0){
m_bExist = false;
}
return m_bExist;
}
};
こんな感じになりますよね。
動きの関数の中身は、ゲームによって変わると思いますので、あえて書いていません。
この二つを見た場合、
かぶっているものがたくさんありますよね。
変数もそうですが、関数もかぶっています。
それはそうですよね、両方ともキャラクターであり、座標もあればライフもあるのですから。
こういうとき、2回書くのがめんどくさくありませんか?
また、
同じことを2回書く、というのはプログラム上スマートではありません。
こういうときに役に立つのが、『継承』というものなのです。
言葉から説明しても理解しにくいかもしれませんので、まず例から言ってみましょう。
まずは、
両方ともに共通となるクラスを作ります。
中身は、共通する事柄をすべて突っ込めばいいです。
これは、キャラクターの元となるクラスなので、BaseCharacterとでも名づけましょう。
class CBaseCharacter
{
protected:
int m_iHp
float m_fx;
float m_fy;
m_bExist;
public:
CBaseCharavter(): m_fx( 0 ), m_fy( 0 ), m_iHp(10), m_bExist( true){} //引数なしコンストラクタ
CBaseCharacter(float fx, float fy) : m_fx( fx ), m_fy( fy ), m_iHp( 10 ), m_bExist( true ){} //引数ありコンストラクタ
~CBaseCharacter(){}
//死んだかどうか
bool IsDeath()
{
if(m_iHP <= 0){
m_bExist = false;
}
return m_bExist;
}
};
共通のクラスを作りました。
コンストラクタが2つありますが、これは実体を生成した際に、
引数をつければ引数ありコンストラクタが呼ばれ、つけなければ、引数なしコンストラクタが呼ばれるようになります。
『継承』とはよくゲームにもある言葉ですが、「親を引き継ぐ」という意味です。
言葉通り、親のクラスを引き継ぐこととなります。
では一体どうやって引き継ぐのか。
次にPlayerクラスを作成したいと思います。
class CPlayer : public CBaseCharacter
{
public:
CPlayer(){}; // 引数なしコンストラクタ
CPlayer(float fx, float fy) : CBaseCharacter(fx, fy){} //引数ありコンストラクタ
~CPlayer(){}
void PlayerMove
{
//自機の動き
}
void ShotBullet()
{
//弾を撃つ処理
}
};
以上となります。
1つずつ説明していきましょう。
先ずは宣言
class CPlayer : public CBaseCharacter
{
・・・
作ったクラスの後ろに、
: public 継承したいクラス
と書いてますね。
これだけで、継承は終了です。
作ったクラスのことを、派生クラス といい、 継承したいクラス(継承されたクラス)のことを、基底クラス といいます。
派生クラスでは、基底クラスのすべてが継承されます。
よって、CPlayerクラスを宣言したときは、中身に、基底クラスの変数(座標)、やら関数がしっかりと詰まっているのですね。
public はどのように継承するのか という意味で、前々回に説明しましたアクセス指定子と同じです。
privateで継承した場合、継承したのに中身が触れなくなってしまうので意味ないですね(笑
よっぽどのことではない限り、public が当たり前と思ってくださって結構です。
次はコンストラクタです。
基底クラスのコンストラクタに引数がなければ、あまり難しくはないのですが、ある場合、
CPlayer(float fx, float fy) : CBaseCharacter(fx, fy){}
のように、初期化子のときと同じように書かなければなりません。
派生先コンストラクタ(引数) : 基底クラスコンストラクタ(引数){}
ですね。
もちろん、他に初期化したいことがあれば、初期化リストですからカンマを書いて続けていくことが出来ます。
もし、これを設定しない場合は、CBaseCharacterの引数なしコンストラクタが呼ばれます。
また、
コンストラクタの呼ばれる順番は、基底クラス→派生クラス の順番で呼ばれます。
最後に、Enemyクラスを作ってみましょう。
class CEnemy : public CBaseCharacter
{
public
CEnemy(float fx, float fy) : CBaseCharacter(fx, fy){}
~CEnemy(){};
void EnemyMove()
{
//敵の動き
}
};
増えることはこれだけですので見やすいですね。
最後に、アクセス指定子の補足ですが、
private、public の他に、protected というのが存在します。
CBaseCharacter のクラスにありますよね?
これは、public と、private のあいの子のような感じで、
クラス外からはアクセスが出来ないですが、継承先ではアクセスが可能というものです。
いわゆる CBaseCharacter の変数は、外からはアクセスできませんが、派生クラスのCPlayer 、CEnemyではアクセスが可能となるのですね。
もう一度、整理して説明いたしますと、
public: クラス外のようなどこからでもアクセスが可能
private: そのクラス内でしかアクセスが不可
protected: そのクラス内、派生クラスでしか、継承が不可
今回の講座は以上で終了ですが、継承というのは様々なことに使えることができます。
もしこのゲームの場合、弾も座標を持っていますので、座標だけのクラスを作り、描画されるすべてのオブジェクトの基底クラスを作ってみるのもいいかもしれません。
また、キャラクターだけではなく、システム面でも結構使えたりします。
ベースのリストを作り、リスト化したいものはそれを派生するなど…。
後は、皆さんの創造性次第。
いろいろ試してみてください。
中原でした。
PR
COMMENT