関数にポリモーフィズム(多態性)をもたせて動作を観察してみましょう                   

ソーラー「


まず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をつけただけです



};



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* pta;



pta=&Lyliane;

pta->statusDataDisplay();



pta =&Pokky;


pta->statusDataDisplay();



pta =&Sylphy;


pta->statusDataDisplay();



return 0;

}


プログラムの実行結果


リリアーネ

HP 10

MP 5

TP 7

ポッキー

HP 20

MP 3

DP 2

シルフィ

HP 3

MP 18

EP 3




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


GameCharacter* pta;



pta=&Lyliane;

pta->statusDataDisplay();



pta =&Pokky;


pta->statusDataDisplay();



pta =&Sylphy;


pta->statusDataDisplay();


に御注目下さい


ptaの親クラスGameCharacter*型のポインタ変数宣言


GameCharacter* pta;


により


生成される


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


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

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

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



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


親クラスGameCharacter*型のポインタ変数ptaに


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

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

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


が代入されると



             GameCharacter* pta


には


            あたかも


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

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

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


が代入されているような感じになります


いえ 😊正確には


       親クラスGameCharacter*型のポインタ変数ptaに


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

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

の管理しているメモリ


を代表して管理しているアドレス&Lyliane


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

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

の管理しているメモリ


を代表して管理しているアドレス&Pokky



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

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

の管理しているメモリ


を代表して管理しているアドレス&Sylphy


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



このとき


      親クラスGameCharacter*型のポインタ変数ptaは


             ptaという同じ名前で


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

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

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



をあらわすことになります



このことを


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


と呼びます



そして



pta =&Lyliane;


pta->statusDataDisplay();


が実行されると


GameCharacter* ptaに


子クラスHuman型のオブジェクトLylianeの(親クラスGameCharacterから引き継いだ)メンバ変数

Lyliane.name

Lyliane.HP

Lyliane.MP


子クラスHuman型のオブジェクトLylianeの(親クラスから引き継いだ)メンバ関数

Lyliane.statusDataDisplay()


の管理しているメモリ


を代表しているアドレス


         &Lyliane


が代入されているので


pta->statusDataDisplay();


が実行されるとき



(親クラスから引き継いだ)メンバ関数statusDataDisplay()


の定義が用いられそうですが


このとき


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


            😊😊😊仮想関数😊😊😊であり


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


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


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


の定義が用いられることになります


ですので


pta->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


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



リリアーネ

HP 10

MP 5

TP 7



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


☆ちょっとおまけ

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

Lyliane.statusDataDisplay()は

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


Lyliane.TPにアクセスすることができます☆


そして



pta =&Pokky;


pta->statusDataDisplay();


が実行されると


GameCharacter* ptaに


子クラスDragon型のオブジェクトPokkyの(親クラスGameCharacterから引き継いだ)メンバ変数

Pokky.name

Pokky.HP

Pokky.MP


子クラスDragon型のオブジェクトPokkyの(親クラスから引き継いだ)メンバ関数


Pokky.statusDataDisplay()


の管理しているメモリ


を代表しているアドレス


         &Pokky


が代入されているので


pta->statusDataDisplay();


が実行されるとき



(親クラスから引き継いだ)メンバ関数statusDataDisplay()


の定義が用いられそうですが


このとき


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



          😊😊😊仮想関数😊😊😊であり



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


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


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


の定義が用いられることになります


ですので


pta->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.TP


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



ポッキー

HP 20

MP 3

DP 2



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


☆ちょっとおまけ

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

Pokky.statusDataDisplay()は

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


Pokky.DPにアクセスすることができます☆






そして



pta =&Sylphy;


pta->statusDataDisplay();


が実行されると


GameCharacter* ptaに


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


pta->statusDataDisplay();


が実行されるとき


pta->



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


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


このとき


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



            😊😊😊仮想関数😊😊😊であり



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


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


pta->は


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



ですので


pta->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


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



シルフィ

HP 3

MP 18

EP 3



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



つまり


pta->statusDataDisplay()



名前は同じでも


異なる子クラス型のオブジェクトの異なる子クラスのメンバ関数


をあらわしています



pta->statusDataDisplay()




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


を備えているというわけです




もう1つポリモーフィズム(多態性)が用いられているプログラムをご覧ください



#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* pta);


};


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

void Human::statusDataDisplay() {


cout << name << "\n";

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

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

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

}




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



void Human::scanGameCharacterData(GameCharacter* pta) {

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

cout << name << "は" << pta->name << "のスキャンを開始した" << "\n";

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


pta->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



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


Lyliane.scanGameCharacterData(&Pokky);

Lyliane.scanGameCharacterData(&Sylphy);

Lyliane.scanGameCharacterData(&Lyliane);


に御注目下さい



クラスHumanのメンバ関数scanGameCharacterData(GameCharacter* pta)の定義


👇

void Human::scanGameCharacterData(GameCharacter* pta) {

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

cout << name << "は" << pta->name << "のスキャンを開始した" << "\n";

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


pta->statusDataDisplay();


}


👆



引数部分に


             GameCharacter* pta



すなわち


親クラスGameCharacter*型のポインタ変数ptaをもちいているので


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

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

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



引数部分の


             GameCharacter* pta


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


親クラスGameCharacter*型のポインタ変数ptaに


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

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

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


が代入されると



             GameCharacter* pta


には


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

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

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


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


このとき



            GameCharacter型のポインタ変数ptaは


同じ名前で


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

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

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



をあらわしています



これも


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


の一種ですね



そして



Lyliane.scanGameCharacterData(&Pokky);


が実行されると


クラスHumanのメンバ関数scanGameCharacterData(GameCharacter* pta)の定義

👇




void Human::scanGameCharacterData(GameCharacter* pta) {

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

cout << name << "は" << pta->name << "のスキャンを開始した" << "\n";

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


pta->statusDataDisplay();


}

👆


nameにLyliane.name


pta->nameにPokky.name


が代入されたものが


まず


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


そして


GameCharacter* ptaに


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


pta->statusDataDisplay();


が実行されるとき


pta->



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


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


このとき


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



            😊😊😊仮想関数😊😊😊であり



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


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


pta->は


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



ですので


pta->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


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


スキャン発動!

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

ステータスデータ

ポッキー

HP 20

MP 3

DP 2



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




そして



Lyliane.scanGameCharacterData(&Sylphy);


が実行されると


クラスHumanのメンバ関数scanGameCharacterData(GameCharacter* pta)の定義

👇




void Human::scanGameCharacterData(GameCharacter* pta) {

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

cout << name << "は" << pta->name << "のスキャンを開始した" << "\n";

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


pta->statusDataDisplay();


}

👆


nameにLyliane.name


pta->nameにSylphy.name


が代入されたものが


まず


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


そして


GameCharacter* ptaに


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


pta->statusDataDisplay();


が実行されるとき


pta->



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


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


このとき


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



        😊😊😊仮想関数😊😊😊であり



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


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


pta->は


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



ですので


pta->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


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


スキャン発動!

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

ステータスデータ

シルフィ

HP 3

MP 18

EP 3



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



そして



Lyliane.scanGameCharacterData(&Lyliane);


が実行されると


クラスHumanのメンバ関数scanGameCharacterData(GameCharacter* pta)の定義

👇




void Human::scanGameCharacterData(GameCharacter* pta) {

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

cout << name << "は" << pta->name << "のスキャンを開始した" << "\n";

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


pta->statusDataDisplay();


}

👆


nameにLyliane.name


pta->nameにLyliane.name


が代入されたものが


まず


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


そして


GameCharacter* ptaに


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


pta->statusDataDisplay();


が実行されるとき


pta->



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


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


このとき


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


           😊😊😊仮想関数😊😊😊であり


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


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


pta->は


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



ですので


pta->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


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


スキャン発動!

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

ステータスデータ

リリアーネ

HP 10

MP 5

TP 7



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



つまり


pta->statusDataDisplay()



名前は同じでも


ptaに代入されるアドレスによって


異なる子クラス型のオブジェクトの異なる子クラスのメンバ関数


をあらわしています



pta->statusDataDisplay()




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


を備えているというわけです」













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

作者を応援しよう!

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

応援したユーザー

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

新規登録で充実の読書を

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

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

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