プログラムのある部分全体にポリモーフィズム(多態性)を持たせてみましょう

ポリモーフィズムをもちいてキャラクターのステータスデータの表示される順番をいれかえてみよう 命令文全体にポリモーフィズム(多態性)を持たせてみます

ソーラー「今日は


        ポリモーフィズムをもちいて


ゲームキャラクターのステータスデータの表示される順番をいれかえてみよう~~😊😊


ゲームによっては


ゲーム内でゲームキャラクターの並び替えをおこなうことができるものがあります


ゲームキャラクターの並び替えをおこなうと


ゲームキャラクターのステータスデータの表示される順番が


リリアーネ

HP 10

MP 5

TP 7

ポッキー

HP 20

MP 3

DP 2

シルフィ

HP 3

MP 18

EP 3


から


ポッキー

HP 20

MP 3

DP 2

シルフィ

HP 3

MP 18

EP 3

リリアーネ

HP 10

MP 5

TP 7


に変化するというわけです


今日はポリモーフィズムをもちいて


ゲームキャラクターのステータスデータの表示される順番を入れ替えてみたいと思います


まず1つの親クラスGameCharacterと


親クラスGameCharacterのメンバ変数宣言、メンバ関数宣言を


継承した


子クラスを


3つ用意します


まずは


親クラスGameCharacterのクラス宣言と


親クラスGameCharacterのメンバ関数statusDataDisplay()


の定義を

             

👇

____________________________________


class GameCharacter {


public:

string name;

int HP;

int MP;


void statusDataDisplay();


};



void GameCharacter::statusDataDisplay() {


cout << name << "\n";

cout << "HP " << HP << "\n";

cout << "MP " << MP << "\n";

}



____________________________________


として


1つ目の


子クラスである


Humanのクラス宣言と


子クラスHumanのメンバ関数


statusDataDisplay()


の定義を

👇

          

_____________________________________________________________________

class Human :public GameCharacter {

public:

int TP;//👈Human(人間)型のキャラクターだけに備わっているテクニックポイントです

void statusDataDisplay();//🌞親クラスGameCharacterのメンバ関数statusDataDisplay()のオーバーライドを行っています


void Human::statusDataDisplay() {


cout << name << "\n";

cout << "HP " << HP << "\n";

cout << "MP " << MP << "\n";

cout << "TP " << TP << "\n";

}




____________________________________


とします。」






2つ目の


子クラスである


Dragonのクラス宣言と


子クラスDragonのメンバ関数


statusDataDisplay()


の定義を

👇

          

_____________________________________________________________________

class Dragon :public GameCharacter {

public:


int DP;//👈ドラゴン型のキャラクターだけに備わっているドラゴンポイントです

void statusDataDisplay();//🌞親クラスGameCharacterのメンバ関数statusDataDisplay()のオーバーライドを行っています



};



//👇🌞🌞🌞子クラスDragonのメンバ関数statusDataDisplay() の定義です🌞🌞🌞

void Dragon::statusDataDisplay() {


cout << name << "\n";

cout << "HP " << HP << "\n";

cout << "MP " << MP << "\n";

cout << "DP " << DP << "\n";

}



____________________________________


とします。」







ソーラー「


3つ目の


子クラスである


Elfのクラス宣言と


子クラスElfのメンバ関数


statusDataDisplay()


の定義を

👇

          

_____________________________________________________________________

class Elf:public GameCharacter {

public:


int EP;//👈エルフ型のキャラクターだけに備わっているエルフポイントです

void statusDataDisplay();

//🌞親クラスGameCharacterのメンバ関数statusDataDisplay()をオーバーライドしています

};



//👇🌞🌞🌞子クラスElfのメンバ関数statusDataDisplay() の定義です🌞🌞🌞

void Elf::statusDataDisplay() {


cout << name << "\n";

cout << "HP " << HP << "\n";

cout << "MP " << MP << "\n";

cout << "EP " << EP << "\n";

}


____________________________________


とします。」




これらのクラスが用いられたプログラムをご覧ください

👇


#include <iostream>

#include <string>//文字列を取り扱うためにヘッダファイル <string>をインクルードしています

using namespace std;


class GameCharacter {


public:

string name;

int HP;

int MP;


virtual void statusDataDisplay();//🌞statusDataDisplay()を仮想関数に設定しました



};



void GameCharacter::statusDataDisplay() {


cout << name << "\n";

cout << "HP " << HP << "\n";

cout << "MP " << MP << "\n";


}



class Human :public GameCharacter {

public:

int TP;//👈Human(人間)型のキャラクターだけに備わっているテクニックポイントです

void statusDataDisplay();//🌞親クラスGameCharacterのメンバ関数statusDataDisplay()のオーバーライドを行っています




};


//👇🌞🌞🌞子クラスHumanのメンバ関数statusDataDisplay() の定義です🌞🌞🌞

void Human::statusDataDisplay() {


cout << name << "\n";

cout << "HP " << HP << "\n";

cout << "MP " << MP << "\n";

cout << "TP " << TP << "\n";

}




class Dragon :public GameCharacter {

public:


int DP;//👈ドラゴン型のキャラクターだけに備わっているドラゴンポイントです

void statusDataDisplay();//🌞親クラスGameCharacterのメンバ関数statusDataDisplay()のオーバーライドを行っています


};



//👇🌞🌞🌞子クラスDragonのメンバ関数statusDataDisplay() の定義です🌞🌞🌞

void Dragon::statusDataDisplay() {


cout << name << "\n";

cout << "HP " << HP << "\n";

cout << "MP " << MP << "\n";

cout << "DP " << DP << "\n";

}



class Elf :public GameCharacter {

public:


int EP;//👈エルフ型のキャラクターだけに備わっているエルフポイントです

void statusDataDisplay();

//🌞親クラスGameCharacterのメンバ関数statusDataDisplay()のオーバーライドを行っています

};



//👇🌞🌞🌞子クラスElfのメンバ関数statusDataDisplay() の定義です🌞🌞🌞

void Elf::statusDataDisplay() {


cout << name << "\n";

cout << "HP " << HP << "\n";

cout << "MP " << MP << "\n";

cout << "EP " << EP << "\n";

}



int main() {


Human Lyliane;

Lyliane.name = "リリアーネ";

Lyliane.HP = 10;

Lyliane.MP = 5;

Lyliane.TP = 7;


Dragon Pokky;

Pokky.name = "ポッキー";

Pokky.HP = 20;

Pokky.MP = 3;

Pokky.DP = 2;


Elf Sylphy;

Sylphy.name = "シルフィ";

Sylphy.HP = 3;

Sylphy.MP = 18;

Sylphy.EP = 3;


GameCharacter* a[3];


a[0] = &Lyliane;

a[1] = &Pokky;

a[2] = &Sylphy;



//👇🌞のちにゲームキャラクターのステータスデータ表示の順番を変更しますが👇この命令文たちには手を加えず変更しません



a[0]->statusDataDisplay();


a[1]->statusDataDisplay();


a[2]->statusDataDisplay();


//👆🌞のちにゲームキャラクターのステータスデータ表示の順番を変更しますが👆この命令文たちには手を加えず変更しません


return 0;

}



プログラムの実行結果


リリアーネ

HP 10

MP 5

TP 7

ポッキー

HP 20

MP 3

DP 2

シルフィ

HP 3

MP 18

EP 3




ソーラー「このプログラムの


GameCharacter* a[3];


a[0] = &Lyliane;

a[1] = &Pokky;

a[2] = &Sylphy;




に御注目下さい


aの親クラスGameCharacter*型の配列宣言


GameCharacter* a[3];


により


生成される


親クラスGameCharacter*型のポインタ変数である配列変数


a[0]

a[1]

a[2]



子クラスHuman型のオブジェクトLylianeのアドレス&Lyliane

子クラスDragon型のオブジェクトPokkyのアドレス&Pokky

子クラスElf型のオブジェクトSylphyのアドレス&Sylphy




代入することができています


親クラスGameCharacter*型のポインタ変数である配列変数


a[0]

a[1]

a[2]



子クラスHuman型のオブジェクトLylianeのアドレス&Lyliane

子クラスDragon型のオブジェクトPokkyのアドレス&Pokky

子クラスElf型のオブジェクトSylphyのアドレス&Sylphy


が代入された状態で



(正しい理解ではありませんが

親クラスGameCharacter*型のポインタ変数である配列変数


a[0]

a[1]

a[2]


には


親クラスGameCharacter型のオブジェクトLylianeのアドレス&Lyliane

親クラスGameCharacter型のオブジェクトPokkyのアドレス&Pokky

親クラスGameCharacter型のオブジェクトSylphyのアドレス&Sylphy



が代入されているとみなすこともできます)



a[0]->statusDataDisplay();


が実行されると



💖💖💖GameCharacter*のポインタ変数である 💖💖💖



💖💖💖a[0]に 💖💖💖


子クラスHuman型のオブジェクトLylianeのアドレス&Lylianeが代入されているので


a[0]->statusDataDisplay();


が実行されるとき


a[0]->



親クラスGameCharacterのメンバ関数statusDataDisplay()


の定義にアクセスすることになりそうですが


このとき


親クラスGameCharacterのメンバ関数statusDataDisplay()は純粋仮想関数であり


子クラスHumanのメンバ関数statusDataDisplay()によって


オーバーライドされているので


a[0]->は


子クラスHumanのメンバ関数statusDataDisplay()の定義にアクセスすることになります



ですので


a[0]->statusDataDisplay();


が実行されると


子クラスHumanのメンバ関数statusDataDisplay()の定義


void Human::statusDataDisplay() {


cout << name << "\n";

cout << "HP " << HP << "\n";

cout << "MP " << MP << "\n";

cout << "TP " << TP << "\n";

}



name

HP

MP

TP



子クラスHuman型のオブジェクトLylianeのメンバ変数


Lyliane.name

Lyliane.HP

Lyliane.MP

Lyliane.TP


が代入されたものが実行され



リリアーネ

HP 10

MP 5

TP 7



が表示されることになります



次に

a[1]->statusDataDisplay();


が実行されると



💖💖💖GameCharacter*のポインタ変数である 💖💖💖



💖💖💖a[1]に 💖💖💖




子クラスDragon型のオブジェクトPokkyのアドレス&Pokkyが代入されているので


a[1]->statusDataDisplay();


が実行されるとき


a[1]->



親クラスGameCharacterのメンバ関数statusDataDisplay()


の定義にアクセスすることになりそうですが


このとき


親クラスGameCharacterのメンバ関数statusDataDisplay()は純粋仮想関数であり


子クラスDragonのメンバ関数statusDataDisplay()によって


オーバーライドされているので


a[1]->は


子クラスDragonのメンバ関数statusDataDisplay()の定義にアクセスすることになります



ですので


a[1]->statusDataDisplay();


が実行されると


子クラスDragonのメンバ関数statusDataDisplay()の定義


void Dragon::statusDataDisplay() {


cout << name << "\n";

cout << "HP " << HP << "\n";

cout << "MP " << MP << "\n";

cout << "DP " << DP << "\n";

}



name

HP

MP

DP



子クラスDragon型のオブジェクトのメンバ変数


Pokky.name

Pokky.HP

Pokky.MP

Pokky.DP


が代入されたものが実行され



ポッキー

HP 20

MP 3

DP 2



が表示されることになります




次に


a[2]->statusDataDisplay();


が実行されると


💖💖💖GameCharacter*のポインタ変数である 💖💖💖



💖💖💖a[2]に 💖💖💖



親クラスGameCharacter型のオブジェクトSylphyのアドレス&Sylphyが代入されているので


a[2]->statusDataDisplay();


が実行されるとき


a[2]->



親クラスGameCharacterのメンバ関数statusDataDisplay()


の定義にアクセスすることになりそうですが


このとき


親クラスGameCharacterのメンバ関数statusDataDisplay()は純粋仮想関数であり


子クラスElfのメンバ関数statusDataDisplay()によって


オーバーライドされているので


a[2]->は


子クラスElfのメンバ関数statusDataDisplay()の定義にアクセスすることになります



ですので


a[2]->statusDataDisplay();


が実行されると


子クラスElfのメンバ関数statusDataDisplay()の定義


void Elf::statusDataDisplay() {


cout << name << "\n";

cout << "HP " << HP << "\n";

cout << "MP " << MP << "\n";

cout << "EP " << EP << "\n";

}



name

HP

MP

EP


子クラスElf型のオブジェクトSylphyのメンバ変数


Sylphy.name

Sylphy.HP

Sylphy.MP

Sylphy.EP


が代入されたものが実行され



シルフィ

HP 3

MP 18

EP 3



が表示されることになります



つまり


親クラスのメンバ関数statusDataDisplay()がj純粋仮想関数に設定されているので

(仮想関数に設定されていても同じです)

GameCharacter*のポインタ変数である 💖💖💖



💖💖💖a[0],a[1],a[2]に 💖💖💖


子クラスHuman型のオブジェクトLylianeのアドレス&Lyliane

子クラスDragon型のオブジェクトPokkyのアドレス&Pokky

子クラスElf型のオブジェクトSylphyのアドレス&Sylphy


を代入して


a[0]->statusDataDisplay();


a[1]->statusDataDisplay();


a[2]->statusDataDisplay();



が実行されるとき


オーバーライドが起こり


それぞれ別の子クラスの(オーバーライドしている)メンバ関数が実行されることになります





           今度は



         ポリモーフィズム(多態性)



を用いて


プログラムの実行結果


リリアーネ

HP 10

MP 5

TP 7

ポッキー

HP 20

MP 3

DP 2

シルフィ

HP 3

MP 18

EP 3


とゲームキャラクターのステータスデータが表示されている状態から


プログラムの実行結果


ポッキー

HP 20

MP 3

DP 2

シルフィ

HP 3

MP 18

EP 3

リリアーネ

HP 10

MP 5

TP 7



ゲームキャラクターのステータスデータが表示されている状態に


入れ替えてみたいと思います」


マックス「 


        ポリモーフィズム(多態性)


をもちいてか?


a[0]->statusDataDisplay();


a[1]->statusDataDisplay();


a[2]->statusDataDisplay();



すでに


         ポリモーフィズム(多態性)


をそなえているんじゃないか?」


ソーラー「今のプログラムでは


         ポリモーフィズム(多態性)


はもちいられていません


単にメンバ関数statusDataDisplay()のオーバーライドがおこっているだけです


確かに

a[0]

a[1]

a[2]

に代入されるアドレスを


変化させれば


a[0]->statusDataDisplay();


a[1]->statusDataDisplay();


a[2]->statusDataDisplay();



異なる関数を実行することになります


a[0]->statusDataDisplay();


a[1]->statusDataDisplay();


a[2]->statusDataDisplay();



      ポリモーフィズム(多態性)を


備えていますが


今は


a[0]

a[1]

a[2]

に代入されるアドレスを


変化させたわけではないので



      ポリモーフィズム(多態性)


が用いられたプログラムではないわけです」



マックス「なるほどな😊 そういうことか」



ソーラー「さて


そうだね


今度は



a[0]->statusDataDisplay();


a[1]->statusDataDisplay();


a[2]->statusDataDisplay();


の部分には


変更を加えずに


a[0]

a[1]

a[2]


に格納されているアドレスを


a[0] = &Lyliane;

a[1] = &Pokky;

a[2] = &Sylphy;


から

a[0] = &Sylphy;

a[1] = &Pokky;

a[2] =&Lyliane;


に変更することにより


プログラムの実行結果


ポッキー

HP 20

MP 3

DP 2

シルフィ

HP 3

MP 18

EP 3

リリアーネ

HP 10

MP 5

TP 7



を表示してみたいと思います


            

             この




a[0]->statusDataDisplay();


a[1]->statusDataDisplay();


a[2]->statusDataDisplay();


の部分に


変更を加えないというところがポイントなんだよ」


そのプログラムはこちらです

👇


#include <iostream>

#include <string>//文字列を取り扱うためにヘッダファイル <string>をインクルードしています

using namespace std;


class GameCharacter {


public:

string name;

int HP;

int MP;


virtual void statusDataDisplay();//🌞ここにvirtualをつけただけです



};



void GameCharacter::statusDataDisplay() {


cout << name << "\n";

cout << "HP " << HP << "\n";

cout << "MP " << MP << "\n";


}



class Human :public GameCharacter {

public:

int TP;//👈Human(人間)型のキャラクターだけに備わっているテクニックポイントです

void statusDataDisplay();//🌞メンバ関数statusDataDisplay()のオーバーライドを行っています




};


//👇🌞🌞🌞クラスHumanのメンバ関数statusDataDisplay() の定義です🌞🌞🌞

void Human::statusDataDisplay() {


cout << name << "\n";

cout << "HP " << HP << "\n";

cout << "MP " << MP << "\n";

cout << "TP " << TP << "\n";

}




class Dragon :public GameCharacter {

public:


int DP;//👈ドラゴン型のキャラクターだけに備わっているドラゴンポイントです

void statusDataDisplay();


};



//👇🌞🌞🌞クラスDragonのメンバ関数statusDataDisplay() の定義です🌞🌞🌞

void Dragon::statusDataDisplay() {


cout << name << "\n";

cout << "HP " << HP << "\n";

cout << "MP " << MP << "\n";

cout << "DP " << DP << "\n";

}



class Elf :public GameCharacter {

public:


int EP;//👈エルフ型のキャラクターだけに備わっているエルフポイントです

void statusDataDisplay();

//親クラスGameCharacterのメンバ関数statusDataDisplay()をオーバーライドしています

};



//👇🌞🌞🌞クラスElfのメンバ関数statusDataDisplay() の定義です🌞🌞🌞

void Elf::statusDataDisplay() {


cout << name << "\n";

cout << "HP " << HP << "\n";

cout << "MP " << MP << "\n";

cout << "EP " << EP << "\n";

}



int main() {


Human Lyliane;

Lyliane.name = "リリアーネ";

Lyliane.HP = 10;

Lyliane.MP = 5;

Lyliane.TP = 7;


Dragon Pokky;

Pokky.name = "ポッキー";

Pokky.HP = 20;

Pokky.MP = 3;

Pokky.DP = 2;


Elf Sylphy;

Sylphy.name = "シルフィ";

Sylphy.HP = 3;

Sylphy.MP = 18;

Sylphy.EP = 3;


GameCharacter* a[3];


a[0] = &Lyliane;

a[1] = &Pokky;

a[2] = &Sylphy;


//👇代入されるアドレスをa[0]とa[2]で入れ替えました


a[0] = &Sylphy;

a[1] = &Pokky;

a[2] = &Lyliane;




//👇🌞ゲームキャラクターのステータスデータ表示の順番を変更しても👇この命令文たちには手を加えず変更しません




a[0]->statusDataDisplay();


a[1]->statusDataDisplay();


a[2]->statusDataDisplay();


//👆🌞ゲームキャラクターのステータスデータ表示の順番を変更しても👆この命令文たちには手を加えず変更しません


return 0;

}


プログラムの実行結果


ポッキー

HP 20

MP 3

DP 2

シルフィ

HP 3

MP 18

EP 3

リリアーネ

HP 10

MP 5

TP 7




ソーラー「ふふ  やったね!😊



a[0]->statusDataDisplay();


a[1]->statusDataDisplay();


a[2]->statusDataDisplay();


の部分を変更しなくても


a[0]

a[1]

a[2]


に格納されているアドレスを


a[0] = &Lyliane;

a[1] = &Pokky;

a[2] = &Sylphy;


から

a[0] = &Sylphy;

a[1] = &Pokky;

a[2] =&Lyliane;


に変更するだけで


プログラムの実行結果


リリアーネ

HP 10

MP 5

TP 7

ポッキー

HP 20

MP 3

DP 2

シルフィ

HP 3

MP 18

EP 3


と表示されていたものを


プログラムの実行結果


ポッキー

HP 20

MP 3

DP 2

シルフィ

HP 3

MP 18

EP 3

リリアーネ

HP 10

MP 5

TP 7



に変更して表示することができました




        ポリモーフィズムをもちいて


ゲームキャラクターのステータスデータの表示される順番をいれかえることができました


a[0]->statusDataDisplay();

a[1]->statusDataDisplay();

a[2]->statusDataDisplay();




それぞれ


      ポリモーフィズム(多態性)


を備えています


また


//👇この部分全体


a[0]->statusDataDisplay();


a[1]->statusDataDisplay();


a[2]->statusDataDisplay();


//👆この部分全体



        同じまま



別の命令を実行することができたわけです


となると



//👇この部分全体


a[0]->statusDataDisplay();


a[1]->statusDataDisplay();


a[2]->statusDataDisplay();


//👆この部分全体




         ポリモーフィズム(多態性)を


備えているといえるんだね」


  • Twitterで共有
  • Facebookで共有
  • はてなブックマークでブックマーク

作者を応援しよう!

ハートをクリックで、簡単に応援の気持ちを伝えられます。(ログインが必要です)

応援したユーザー

応援すると応援コメントも書けます

新規登録で充実の読書を

マイページ
読書の状況から作品を自動で分類して簡単に管理できる
小説の未読話数がひと目でわかり前回の続きから読める
フォローしたユーザーの活動を追える
通知
小説の更新や作者の新作の情報を受け取れる
閲覧履歴
以前読んだ小説が一覧で見つけやすい
新規ユーザー登録無料

アカウントをお持ちの方はログイン

カクヨムで可能な読書体験をくわしく知る