親クラス&型の参照変数に子クラス型のオブジェクトを何回も代入する場合はご注意ください オーバーライドがうまく実行できなくなります

親クラス&型の参照変数が親もしくは子クラス型のオブジェクトによって初期化されると参照変数はその最初に初期化に用いられたオブジェクトの管理しているメモリ領域にアクセスし続けることになります


ソーラー「親クラス&型の参照変数に親クラスまたは子クラス型のオブジェクトが


代入されると


親クラス&型の参照変数は


その親クラスまたは子クラス型のオブジェクトの管理するメモリ領域にアクセスし続けることに事になります


いいかえると


親クラス&型の参照変数は


親クラスまたは子クラス型のオブジェクトそのものをあらわすことになります


そして


あらたに


その親クラス&型の参照変数に


別の親クラスまたは子クラス型のオブジェクトを代入しても


その別の親クラスまたは子クラス型のオブジェクトの持っているデータだけが


親クラス&型の参照変数に代入される


               💖値渡し💖


が行われることになります


つまり


親クラス&型の参照変数はその別の親クラスまたは子クラス型のオブジェクトそのものをあらわすことにはなりません



親クラス&型の参照変数はその別の親クラスまたは子クラス型のオブジェクトの管理するメモリ領域にアクセスすることはありません


ってことを学んでみよう」


マックス「 お、おい 何の話だ(;´∀`)」


ソーラー「


まず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();//🌞メンバ関数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();


};



//👇🌞🌞🌞クラス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();//🌞virtualをつけ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();//🌞メンバ関数statusDataDisplay()のオーバーライドを行っています




};


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

void Human::statusDataDisplay() {


cout << name << "\n";

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

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

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

}




//👇🌞🌞🌞クラスHumanのメンバ関数scanGameCharacterData(GameCharacter& a)の定義です🌞🌞🌞





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=Lyliane;

a.statusDataDisplay();



a=Pokky;


a.statusDataDisplay();



a=Sylphy;


a.statusDataDisplay();



return 0;

}


プログラムの実行結果


リリアーネ

HP 10

MP 5

TP 7

ポッキー

HP 20

MP 3

TP 7

シルフィ

HP 3

MP 18

TP 7



マックス「な、なんかこのプログラムの実行結果


おかしくないか?


なんで


プログラムの実行結果


リリアーネ

HP 10

MP 5

TP 7👈ここです

ポッキー

HP 20

MP 3

TP 7👈ここです

シルフィ

HP 3

MP 18

TP 7👈ここです




リリアーネ

ポッキー

シルフィ


ステータスデータに全部


TP 7




表示されている????????」



ソーラー「そうなんです


今までの流れでいうと


リリアーネ

HP 10

MP 5

TP 7👈ここです

ポッキー

HP 20

MP 3

DP 2👈ここです

シルフィ

HP 3

MP 18

EP 3👈ここです


が表示されなければならないはずなんです


今のプログラムの


GameCharacter& a=Lyliane;

a.statusDataDisplay();



a=Pokky;


a.statusDataDisplay();



a=Sylphy;


a.statusDataDisplay();


の部分に御注目下さい


aの親クラスGameCharacter&型の参照変数宣言、初期化


GameCharacter& a=Lyliane;


により


生成される


親クラスGameCharacter&型の参照変数であるaに


子クラスHuman型のオブジェクトLyliane

子クラスDragon型のオブジェクトPokky

子クラスElf型のオブジェクトSylphy




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


親クラスGameCharacter&型の参照変数aに


子クラスHuman型のオブジェクト

子クラスDragon型のオブジェクト

子クラスElf型のオブジェクト



が代入されると



通常



親クラスGameCharacter&型の参照変数aでは



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

子クラスHuman型のオブジェクトの()メンバ関数



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

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



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

子クラスElf型のオブジェクトの()メンバ関数


の管理しているメモリ領域にだけ


アクセスできることになります


そして



GameCharacter& a=Lyliane;

a.statusDataDisplay();


が実行されると


GameCharacter& aに


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


a.statusDataDisplay();


が実行されるとき


親クラスGameCharacter&型の参照変数aでは


子クラスHuman型のオブジェクトLylianeの()メンバ関数statusDataDisplay()のメモリ領域


にしかアクセスできないので



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


の定義がもちいられそうですが



()メンバ関数statusDataDisplay()は


          🌞仮想関数であり🌞


子クラスHumanの(子クラスHumanに元から備わっている)メンバ関数statusDataDisplay()によって


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


a.statusDataDisplay();


が実行されるとき


子クラスHumanの(子クラスHumanに元から備わっている)メンバ関数statusDataDisplay()の定義が用いられることになります


つまり


親クラスGameCharacter&型の参照変数aは


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

Lyliane.name

Lyliane.HP

Lyliane.MP

の管理するメモリ領域だけ


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


Lyliane.statusDataDisplay()


の管理するメモリ領域だけにアクセスすることができます



ですので


main関数内で


親クラスGameCharacter&型の参照変数aに.ドット演算子を用いた


a.name

a.HP

a.MP



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

Lyliane.name

Lyliane.HP

Lyliane.MP

の管理するメモリにアクセスすることができますが


親クラスGameCharacter&型の参照変数aに.ドット演算子を用いた


a.TP

では


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

Lyliane.TP

の管理するメモリにアクセスすることはできません


しかし


親クラスGameCharacter&型の参照変数aに.ドット演算子を用いた


a.statusDataDisplay();


なら


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

Lyliane.TP

の管理するメモリ領域に


間接的に


アクセスすることができます


ですので


a.statusDataDisplay();


が実行されると




子クラスHumanの(子クラスHumanに元から備わっている)メンバ関数statusDataDisplay()の定義


void Human::statusDataDisplay() {


cout << name << "\n";

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

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

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

}



name

HP

MP

TP



Lyliane.name

Lyliane.HP

Lyliane.MP

Lyliane.TP


が代入された


cout << Lyliane.name << "\n";

cout << "HP " << Lyliane.HP << "\n";

cout << "MP " << Lyliane.MP << "\n";

cout << "TP " << Lyliane.TP << "\n";



が実行され


コマンドプロンプト画面に



リリアーネ

HP 10

MP 5

TP 7



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




そして


             😊 いよいよ😊


新たに親クラスGameCharacter&型の参照変数aに


子クラスDragon型のオブジェクトPokkyを代入する


a=Pokky;


a.statusDataDisplay();



の実行ですね


えへへ


最初に結論をいってしまうと


a=Pokky;


a.statusDataDisplay();



が実行されると


親クラスGameCharacter&の参照変数aに


子クラスDragon型のオブジェクトPokkyが代入されることになりますが


親クラスGameCharacter&の参照変数aには


子クラスDragon型のオブジェクトPokkyの持っているデータだけが


渡される


        😊値渡しがおこなわれることになります😊



親クラスGameCharacter&の参照変数aが


子クラスDragon型のオブジェクトPokkyそのものをあらわすことにはなりません



〇  〇  〇  〇  〇  〇  〇  〇  〇  〇  〇  〇  〇

これが


参照変数の基本的な性質なんです


参照変数が代入された変数そのものになる


参照変数が代入された変数の管理しているメモリにアクセスするようになるのは


その変数によって


初めて


参照変数が初期化されたときだけなんです


2回目以降に参照変数に変数が代入される場合は


変数の持っているデータだけが


参照変数の管理しているメモリに


             値渡し


されることになります

〇  〇  〇  〇  〇  〇  〇  〇  〇  〇  〇  〇  〇



親クラスGameCharacter&の参照変数aに


子クラスDragon型のオブジェクトPokkyが代入されていますが



実際には


親クラスGameCharacter&の参照変数aが管理しているメモリは


子クラスHuman型のオブジェクトLylianeの管理しているメモリなんです



ですので





a.statusDataDisplay();


が実行されるとき



            通常


親クラスGameCharacter&型の参照変数では


子クラスHuman型のオブジェクトLylianeの(GameCharacter)メンバ関数statusDataDisplay()の管理するメモリ領域


にしかアクセスできないので



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


の定義がもちいられそうですが



()メンバ関数statusDataDisplay()は



           🌞仮想関数であり🌞


子クラスHumanの(子クラスHumanに元から備わっている)メンバ関数statusDataDisplay()によって


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


a.statusDataDisplay();


が実行されるとき


子クラスHumanの(子クラスHumanに元から備わっている)メンバ関数statusDataDisplay()の定義が用いられることになります



ですので


a.statusDataDisplay();


が実行されると


子クラスHumanの(子クラスHumanに元から備わっている)メンバ関数statusDataDisplay()の定義


void Human::statusDataDisplay() {


cout << name << "\n";

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

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

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

}



name

HP

MP

TP



Lyliane.name

Lyliane.HP

Lyliane.MP

Lyliane.TP


が代入された


cout << Lyliane.name << "\n";

cout << "HP " << Lyliane.HP << "\n";

cout << "MP " << Lyliane.MP << "\n";

cout << "TP " << Lyliane.TP << "\n";



が実行されることになります


新たに親クラスGameCharacter&型の参照変数aに


子クラスDragon型のオブジェクトPokkyを代入する


a=Pokky;


が実行されているので


Lyliane.name

Lyliane.HP

Lyliane.MP


には


"ポッキー"

20

3


が代入されているので


(Lyliane.TPに代入されているデータは2のまま変化していませんね)


コマンドプロンプト画面に



ポッキー

HP 20

MP 3

TP 2



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


マックス「そりゃ、そうなるんじゃないか


いい説明じゃないか」


ソーラー「お話は戻って



a=Pokky;


a.statusDataDisplay();



が実行されると


親クラスGameCharacter&の参照変数aに


子クラスDragon型のオブジェクトPokkyが代入されることになります


親クラスGameCharacter&の参照変数aに


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


a.statusDataDisplay();


が実行されるとき



          通常



親クラスGameCharacter&型の参照変数では


子クラスHuman型のオブジェクトLylianeの(GameCharacter)メンバ関数statusDataDisplay()の管理するメモリ領域


にしかアクセスできないので



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


の定義がもちいられそうですが



()メンバ関数statusDataDisplay()は



           🌞仮想関数であり🌞


子クラスHumanの(子クラスHumanに元から備わっている)メンバ関数statusDataDisplay()によって


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


a.statusDataDisplay();


が実行されるとき


子クラスHumanの(子クラスHumanに元から備わっている)メンバ関数statusDataDisplay()の定義が用いられることになります



ですので


a.statusDataDisplay();


が実行されると


子クラスHumanの(子クラスHumanに元から備わっている)メンバ関数statusDataDisplay()の定義


void Human::statusDataDisplay() {


cout << name << "\n";

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

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

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

}



name

HP

MP

TP



Lyliane.name

Lyliane.HP

Lyliane.MP

Lyliane.TP


が代入された


cout << Lyliane.name << "\n";

cout << "HP " << Lyliane.HP << "\n";

cout << "MP " << Lyliane.MP << "\n";

cout << "TP " << Lyliane.TP << "\n";



が実行されることになります


新たに親クラスGameCharacter&型の参照変数aに


子クラスDragon型のオブジェクトPokkyを代入する


a=Pokky;


が実行されているので


Lyliane.name

Lyliane.HP

Lyliane.MP


には


"ポッキー"

20

3


が代入されているので


(Lyliane.TPに代入されているデータは2のまま変化していませんね)


コマンドプロンプト画面に



ポッキー

HP 20

MP 3

TP 2



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



     ・・・そう・・・



         そうなるんです!




マックス「おおぅ いいねえ」



ソーラー「簡単でした



 💖参照変数aは💖




 💖の管理しているメモリ領域にアクセスし続けるすることになる💖



というのがポイントでした」



マックス「そういうことか」


ソーラー「


まず先ほどのプログラムの流れを再確認してみましょう

👇


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=Lyliane;

a.statusDataDisplay();



a=Pokky;


a.statusDataDisplay();



a=Sylphy;


a.statusDataDisplay();




ここでの注目ポイントは



aの親クラスGameCharacter&型の参照変数宣言、初期化


GameCharacter& a=Lyliane;


です




aの親クラスGameCharacter&型の参照変数宣言が


子クラスHuman型のオブジェクトLylianeによって


初期化される


GameCharacter& a=Lyliane;


が実行されると


参照変数aは 


最初に初期化にもちいられた 


子クラスHuman型のオブジェクトLylianeの管理しているメモリ領域にアクセスし続けることになります


つまり


a.name

a.HP

a.MP


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


Lyliane.name

Lyliane.HP

Lyliane.MP

の管理しているメモリ領域にアクセスし続けることになります


a.statusDataDisplay()



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


Lyliane.statusDataDisplay()


の管理しているメモリ領域にアクセスし続けることになります



            この場合


statusDataDisplay()


はオーバーライドされているので正確には


子クラスHuman型のオブジェクトLylianeの(子クラスHumanに元から備わっている)メンバ関数


Lyliane.statusDataDisplay()


の管理しているメモリ領域にアクセスし続けることになります



マックス「ほいほい」


solarplexuss「ほいほい」


ソーラー「で


次に親クラスGameCharacter&型の参照変数aに


子クラスDragon型のオブジェクトPokkyが代入される


a=Pokky;


a.statusDataDisplay();


が実行されると


何がおきるかということですね



ひとたび


aの親クラスGameCharacter&型の参照変数宣言、初期化


GameCharacter& a=Lyliane;


が実行されると


参照変数aは一度代入された子クラスHuman型のオブジェクトLyliane


の管理しているメモリ領域にアクセスしつづけることになります



つまり


親クラスGameCharacter&型の参照変数aに



           🌞あらたに🌞


子クラスDragon型のオブジェクトPokkyを代入する



a=Pokky;



の実行においては


参照変数aに子クラスDragon型のオブジェクトPokkyのメンバ変数の持っているデータだけが(コピーされる)


渡される


          💖値渡し💖



がおこなわれることになります


つまり


a=Pokky;


が実行されると


a.name

a.HP

a.MP

には


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


Pokky.name

Pokky.HP

Pokky.MP

の格納しているデータ


ポッキー

20

3



a.name="ポッキー";

a.HP=20;

a.MP= 3;


のように代入されることになります


ですが


参照変数aは一度代入された子クラスHuman型のオブジェクトLyliane


の管理しているメモリ領域にアクセスしつづけているので


a.name

a.HP

a.MP



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


Lyliane.name

Lyliane.HP

Lyliane.MP

の管理しているメモリ領域にアクセスし続けていることに変わりはありません


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


Lyliane.name

Lyliane.HP

Lyliane.MP



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


Pokky.name

Pokky.HP

Pokky.MP

の格納しているデータ


ポッキー

20

3


代入されているだけなんです


(実際に


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


Lyliane.name

Lyliane.HP

Lyliane.MP



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


Pokky.name

Pokky.HP

Pokky.MP

の格納しているデータ


ポッキー

20

3

代入されています





そして


a.statusDataDisplay();


の実行においても


aは子クラスHuman型のオブジェクトLylianeの(🌞Human🌞》》)メンバ関数statusDataDisplay()の管理するメモリ領域にアクセスし続けています


ですので


a.statusDataDisplay();


が実行されると



(🌞Human🌞》》)メンバ関数statusDataDisplay()


の定義


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


void Human::statusDataDisplay() {


cout << name << "\n";

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

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

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

}


内の


name

HP

MP


Lyliane.name

Lyliane.HP

Lyliane.MP


が用いられた


cout << Lyliane.name << "\n";

cout << "HP " << Lyliane.HP << "\n";

cout << "MP " << Lyliane.MP << "\n";

cout << "TP " << Lyliane.TP << "\n";


が実行されることになります


Lyliane.name

Lyliane.HP

Lyliane.MP

には


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


Pokky.name

Pokky.HP

Pokky.MP

の格納しているデータ


ポッキー

20

3


代入されており


Lyliane.TPには


以前に


Human Lyliane;

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

Lyliane.HP = 10;

Lyliane.MP = 5;

Lyliane.TP = 7;


を実行したときに


数値データ


7


が代入されているので


a.statusDataDisplay();


が実行されると


プログラムの実行結果



ポッキー

HP 20

MP 3

TP 7


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



ですので


ポッキー

HP 20

MP 3

DP 2


が表示されることはありません




では・・・・・


もうわかったかな?


同様に


a = Sylphy;


a.statusDataDisplay();


が実行されると


何がおきるのかな?


というとですね


aの親クラスGameCharacter&型の参照変数宣言、初期化


GameCharacter& a=Lyliane;


が実行されると


参照変数aは一度代入された子クラスHuman型のオブジェクトLyliane


の管理しているメモリ領域にアクセスしつづけることになります


つまり


親クラスGameCharacter&型の参照変数aに



           🌞あらたに🌞


子クラスElf型のオブジェクトSylphyを代入する



a=Sylphy;



の実行においては


参照変数aに子クラスElf型のオブジェクトSylphyのメンバ変数の持っているデータだけが(コピーされる)


渡される


          💖値渡し💖



がおこなわれることになります




つまり


a=Sylphy;


が実行されると


a.name

a.HP

a.MP

には


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


Sylphy.name

Sylphy.HP

Sylphy.MP

の格納しているデータ


シルフィ

3

18



a.name="シルフィ";

a.HP=3;

a.MP=18;


のように代入されることになります


ですが


参照変数aは一度代入された子クラスHuman型のオブジェクトLyliane


の管理しているメモリ領域にアクセスしつづけているので


a.name

a.HP

a.MP



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


Lyliane.name

Lyliane.HP

Lyliane.MP

の管理しているメモリ領域にアクセスし続けていることにかわりはありません


ですので


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


Lyliane.name

Lyliane.HP

Lyliane.MP



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


Sylphy.name

Sylphy.HP

Sylphy.MP

の格納しているデータ


シルフィ

18

7


代入されることになります



a.statusDataDisplay();


の実行においても


aは子クラスHuman型のオブジェクトLylianeの(🌞Human🌞》》)メンバ関数statusDataDisplay()の管理するメモリ領域にアクセスし続けています


ですので


a.statusDataDisplay();


が実行されると



(🌞Human🌞》》)メンバ関数statusDataDisplay()


の定義


void Human::statusDataDisplay() {


cout << name << "\n";

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

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

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

}


内の


name

HP

MP


Lyliane.name

Lyliane.HP

Lyliane.MP


が用いられた


cout << Lyliane.name << "\n";

cout << "HP " << Lyliane.HP << "\n";

cout << "MP " << Lyliane.MP << "\n";

cout << "TP " << Lyliane.TP << "\n";


が実行されることになります


Lyliane.name

Lyliane.HP

Lyliane.MP

には


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


Sylphy.name

Sylphy.HP

Sylphy.MP

の格納しているデータ


シルフィ

18



代入されており


Lyliane.TPには


Human Lyliane;

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

Lyliane.HP = 10;

Lyliane.MP = 5;

Lyliane.TP = 7;


の実行により


数値データ


7


が代入されているので



a.statusDataDisplay();


が実行されると


プログラムの実行結果



シルフィ

HP 3

MP 18

TP 7


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



シルフィ

HP 20

MP 18

EP 3


が表示されることはありません



わかったかな?


マックス「Ohおう~Ye《いえ~す》s~!」



ソーラー「


           今度は



     🌞🌞🌞自作関数の引数に参照変数が用いられている場合🌞🌞


を観察してみたいと思います


自作関数の


引数となっている参照変数にオブジェクトが代入されて実行,完了されると


参照変数はメモリから消去されることになります


その後、再度


自作関数の


引数となっている参照変数にオブジェクトが代入されて実行されると


参照変数はそのオブジェクトによって新たに初期化され


参照変数は そのオブジェクトの管理するメモリ領域にアクセスすることになります



いいかえると


参照変数は新たに代入されたオブジェクトそのものを表すことになります


そして


自作関数の実行が完了すると参照変数はメモリから消去されることになります


そうなると


参照変数そのものが存在せず

参照変数は新たに代入されたオブジェクトの管理するメモリ領域にアクセスすることはできなくなるね」


マックス「はうう わおおおん~ なんのこと~」



ソーラー「


それでは


その様子がみられる


自作関数の引数に参照変数が用いられたプログラムをご覧ください」



#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()のオーバーライドを行っています


void scanGameCharacterData(GameCharacter& a);


};


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

void Human::statusDataDisplay() {


cout << name << "\n";

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

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

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

}




//👇🌞🌞🌞クラスHumanのメンバ関数scanGameCharacterData(GameCharacter& a)の定義です🌞🌞🌞



void Human::scanGameCharacterData(GameCharacter& a) {

cout << "スキャン発動!" << "\n";

cout << name << "は" << a.name << "のスキャンを開始した" << "\n";

cout << "ステータスデータ" << "\n";


a.statusDataDisplay();


}


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;


Lyliane.scanGameCharacterData(Pokky);

Lyliane.scanGameCharacterData(Sylphy);

Lyliane.scanGameCharacterData(Lyliane);


return 0;

}


プログラムの実行結果


スキャン発動!

リリアーネはポッキーのスキャンを開始した

ステータスデータ

ポッキー

HP 20

MP 3

DP 2

スキャン発動!

リリアーネはシルフィのスキャンを開始した

ステータスデータ

シルフィ

HP 3

MP 18

EP 3

スキャン発動!

リリアーネはリリアーネのスキャンを開始した

ステータスデータ

リリアーネ

HP 10

MP 5

TP 7





マックス「あれ?


ビルド実行結果が


リリアーネ

HP 10

MP 5

TP 7👈ここです

ポッキー

HP 20

MP 3

TP 7👈ここです

シルフィ

HP 3

MP 18

TP 7👈ここです


でなく


スキャン発動!

リリアーネはポッキーのスキャンを開始した

ステータスデータ

ポッキー

HP 20

MP 3

DP 2

スキャン発動!

リリアーネはシルフィのスキャンを開始した

ステータスデータ

シルフィ

HP 3

MP 18

EP 3

スキャン発動!

リリアーネはリリアーネのスキャンを開始した

ステータスデータ

リリアーネ

HP 10

MP 5

TP 7


になっとるぅぅ


にゃんだこれは」



ソーラー「この仕組みはとても単純で


自作関数の実行が完了すると


その関数内で変数宣言された変数はメモリから消去される


ということが関係しています



このプログラムの


Lyliane.scanGameCharacterData(Pokky);

Lyliane.scanGameCharacterData(Sylphy);

Lyliane.scanGameCharacterData(Lyliane);


に御注目下さい


Lyliane.scanGameCharacterData(Pokky);



が実行されると


子クラスHumanのメンバ関数scanGameCharacterData(GameCharacter& a)の定義


👇

void Human::scanGameCharacterData(GameCharacter& a) {

cout << "スキャン発動!" << "\n";

cout << name << "は" << a.name << "のスキャンを開始した" << "\n";

cout << "ステータスデータ" << "\n";


a.statusDataDisplay();


}


👆



引数部分の


             GameCharacter& a



子クラスDragon型のオブジェクトPokky


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


つまり


親クラスGameCharacter&型の参照変数aに


子クラスDragon型のオブジェクトPokky


が代入された


cout << "スキャン発動!" << "\n";

cout << Lyliane.name << "は" << Pokky.name << "のスキャンを開始した" << "\n";

cout << "ステータスデータ" << "\n";


a.statusDataDisplay();


が実行されるので



スキャン発動!

リリアーネはポッキーのスキャンを開始した

ステータスデータ

ポッキー

HP 20

MP 3

DP 2


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


詳しくは


Lyliane.scanGameCharacterData(Pokky);


が実行されると


親クラスGameCharacter&型の参照変数aは


最初に代入される


子クラスDragon型のオブジェクトPokkyの管理するメモリにアクセスし続ける


ことになる


すなわち


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


Pokky.name

Pokky.HP

Pokky.MP


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


Pokky.statusDataDisplay()


の管理するメモリ領域にアクセスすることになります


正確には


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


         😊仮想関数であり😊


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


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


子クラスDragon型のオブジェクトPokkyの


(子クラスDragonに元から備わっている)メンバ関数


Pokky.statusDataDisplay()の管理しているメモリ領域にアクセスすることになります



ですので



Lyliane.scanGameCharacterData(Pokky);


が実行されると


子クラスHumanのメンバ関数scanGameCharacterData(GameCharacter& a)の定義


👇

void Human::scanGameCharacterData(GameCharacter& a) {

cout << "スキャン発動!" << "\n";

cout << name << "は" << a.name << "のスキャンを開始した" << "\n";

cout << "ステータスデータ" << "\n";


a.statusDataDisplay();


}


👆


nameにはLyliane.name

a.nameにはPokky.name

a.statusDataDisplay()には


Pokky.statusDataDisplay()


を代入した


cout << "スキャン発動!" << "\n";

cout << Lyliane.name << "は" << Pokky.name << "のスキャンを開始した" << "\n";

cout << "ステータスデータ" << "\n";


Pokky.statusDataDisplay;



実行されることになります


Pokky.statusDataDisplay();


の実行においては


親クラスGameCharacterのメンバ関数


statusDataDisplay()



仮想関数に設定されており


statusDataDisplay()



(子クラスDragonに元から備わっている)メンバ関数


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



Pokky.name

Pokky.HP

Pokky.MP

Pokky.DP


が代入された


cout << Pokky.name << "\n";

cout << "HP " << Pokky.HP << "\n";

cout << "MP " << Pokky.MP << "\n";

cout << "DP " << Pokky.DP << "\n";


が実行されることになります


Pokky.name

Pokky.HP

Pokky.MP

Pokky.DP

には

ポッキー

20

3

2

が代入されているので


Lyliane.scanGameCharacterData(Pokky);


が実行されると


スキャン発動!

リリアーネはポッキーのスキャンを開始した

ステータスデータ

ポッキー

HP 20

MP 3

DP 2


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



そして


Lyliane.scanGameCharacterData(Pokky);


の実行が終了すると


もちろん


親クラスGameCharacter&型の参照変数a


はメモリから


消去されます


ですので


親クラスGameCharacter&型の参照変数aが


子クラスDragon型のオブジェクトPokkyの管理するメモリ領域にアクセスするなんてことはありません



次に


Lyliane.scanGameCharacterData(Sylphy);


が実行されると


子クラスHumanのメンバ関数scanGameCharacterData(GameCharacter& a)の定義


👇

void Human::scanGameCharacterData(GameCharacter& a) {

cout << "スキャン発動!" << "\n";

cout << name << "は" << a.name << "のスキャンを開始した" << "\n";

cout << "ステータスデータ" << "\n";


a.statusDataDisplay();


}


👆



引数部分の


             GameCharacter& a



子クラスElf型のオブジェクトSylphy


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


つまり


親クラスGameCharacter&型の参照変数aに


子クラスElf型のオブジェクトSylphy


が代入された



cout << "スキャン発動!" << "\n";

cout << Lyliane.name << "は" << Sylphy.name << "のスキャンを開始した" << "\n";

cout << "ステータスデータ" << "\n";


Sylphy.statusDataDisplay();



が実行されるのですが


Sylphy.statusDataDisplay();


の実行においては


親クラスGameCharacterのメンバ関数


statusDataDisplay()



仮想関数に設定されており


statusDataDisplay()



(子クラスElfに元から備わっている)メンバ関数


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



Sylphy.name

Sylphy.HP

Sylphy.MP

Sylphy.EP

が代入された


cout << Sylphy.name << "\n";

cout << "HP " << Sylphy.HP << "\n";

cout << "MP " << Sylphy.MP << "\n";

cout << "EP " << Sylphy.EP << "\n";


が実行されることになります


Sylphy.name

Sylphy.HP

Sylphy.MP

Sylphy.EP

には

シルフィ

3

18

3

が代入されているので


Lyliane.scanGameCharacterData(Pokky);


が実行されると


スキャン発動!

リリアーネはシルフィのスキャンを開始した

ステータスデータ

シルフィ

HP 3

MP 18

EP 3


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



Lyliane.scanGameCharacterData(Sylphy);


の実行が終了すると


もちろん


親クラスGameCharacter&型の参照変数a


はメモリから


消去されます



ですので


親クラスGameCharacter&型の参照変数aが


子クラスElf型のオブジェクトSylphyの管理するメモリ領域にアクセスすることはありません






次に


Lyliane.scanGameCharacterData(Lyliane);


が実行されると


子クラスHumanのメンバ関数scanGameCharacterData(GameCharacter& a)の定義


👇

void Human::scanGameCharacterData(GameCharacter& a) {

cout << "スキャン発動!" << "\n";

cout << name << "は" << a.name << "のスキャンを開始した" << "\n";

cout << "ステータスデータ" << "\n";


a.statusDataDisplay();


}


👆



引数部分の


             GameCharacter& a



子クラスHuman型のオブジェクトLyliane


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


つまり


親クラスGameCharacter&型の参照変数aに


子クラスHuman型のオブジェクトLyliane


が代入された



cout << "スキャン発動!" << "\n";

cout << Lyliane.name << "は" << Lyliane.name << "のスキャンを開始した" << "\n";

cout << "ステータスデータ" << "\n";


Lyliane.statusDataDisplay();



が実行されるのですが


Lyliane.statusDataDisplay();


の実行においては


親クラスGameCharacterのメンバ関数


statusDataDisplay()



仮想関数に設定されており


statusDataDisplay()



(子クラスHumanに元から備わっている)メンバ関数


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



Lyliane.name

Lyliane.HP

Lyliane.MP

Lyliane.TP

が代入された


cout << Lyliane.name << "\n";

cout << "HP " << Lyliane.HP << "\n";

cout << "MP " << Lyliane.MP << "\n";

cout << "DP " << Lyliane.DP << "\n";


が実行されることになります


Lyliane.name

Lyliane.HP

Lyliane.MP

Lyliane.TP

には

リリアーネ

10

5

7


が代入されているので


Lyliane.scanGameCharacterData(Lyliane);


が実行されると


スキャン発動!

リリアーネはリリアーネのスキャンを開始した

ステータスデータ

リリアーネ

HP 10

MP 5

TP 7


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



Lyliane.scanGameCharacterData(Lyliane);


の実行が終了すると


もちろん


親クラスGameCharacter&型の参照変数a


はメモリから


消去されます



ですので


親クラスGameCharacter&型の参照変数aが


子クラスHuman型のオブジェクトLylianeの管理するメモリ領域にアクセスすることはありません」



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

作者を応援しよう!

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

応援したユーザー

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

新規登録で充実の読書を

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

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

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