抽象クラスと純粋仮想関数を導入してみます。

つ・づ・き・ま~す・・・


ソーラー「次は


純粋仮想関数というものを導入してみたいとおもいます。」


マックス「さっき


仮想関数を導入したがそれと何か関係があるのか?」


ソーラー「かなり近い関係にあります。


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



子クラスDragonのクラス宣言内で


同じ名前の


メンバ関数宣言


void statusDataDisplay();


が行われているとします。


つまり


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

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


によって


      🌞オーバーライド🌞


されているとします



でも ちょっとまって



このときの



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



     💖仮想関数に設定されていないとします💖



オーバーライドがおこなわれているだけです



今までのエピソードをご覧になられてもお分かりになられますように


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


GameCharacter* pta;


によって生成される


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



              ptaに



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


Dragon Pokky;


によって生成される


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


             🍋&Pokky🍋


を代入することができます


( 正確ではありませんが


まるで あたかも


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


GameCharacter* pta;


によって生成される


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



              ptaに



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



             🍓&Pokky🍓



が代入されたような感じになります)


正確には



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


GameCharacter* pta;


によって生成される


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



              ptaに






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


Pokky.name

Pokky.HP

Pokky.MP



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


Pokky.statusDataDisplay()


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


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


&Pokky


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


この状態で


pta->statusDataDisplay();


が実行されると


pta->statusDataDisplay()は


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


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


つまり


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


Pokky.statusDataDisplay();


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




そして


今度は




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



メンバ関数宣言


void statusDataDisplay();



virtual void statusDataDisplay();


のように記述することにより


親クラスGameCharacterのメンバ関数


statusDataDisplay()



仮想関数に設定してみます



そして



子クラスDragonのクラス宣言内では


同じ名前statusDataDisplay()の


メンバ関数宣言


void statusDataDisplay();


を設定しておきます

(親クラスGameCharacterのメンバ関数statusDataDisplay()のオーバーライドを

子クラスDragonのメンバ関数statusDataDisplay()により行っています)


この状態で


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


GameCharacter* pta;


によって生成される


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



              ptaに



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


Dragon Pokky;


によって生成される


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


             🍋&Pokky🍋


を代入したとします


この状態で



pta->statusDataDisplay();


を実行したとき


pta->statusDataDisplay()



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

         

Pokky.statusDataDisplay()


の管理するメモリ領域でなく


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


Pokky.statusDataDisplay()


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


つまり


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


Pokky.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のメンバ変数


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";



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


マックス「そ、そうだったな」


ソーラー「ここで



           🌞純粋仮想関数🌞



の登場です。


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



virtual void statusDataDisplay()=0;


のように


=0



くっつけると


statusDataDisplay()は


純粋仮想関数と呼ばれるものになります。」


マックス「純粋仮想関数と


仮想関数でなにか違いがあるのか?」


ソーラー「純粋がつくだけあって


本当に


statusDataDisplay()は


仮想の関数となるんです」


てんC「本当に仮想の関数になるのですか?」


ソーラー「そう、


なぜなら


           純粋仮想関数は


関数の定義を記述しなくてもよい


(関数の定義を設定することはできるんですが)


からなんです。」


マックス「関数の定義がない関数??


そんな関数が存在するのか~~~い


何ですかあ


それはいったいど~いう関数なんだ」


ソーラー「親クラスGameCharacterの



         😊仮想関数😊


statusDataDisplay()が


子クラスDragonのメンバ関数


statusDataDisplay()


によってオーバーライドされているとします


そして


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


子クラスDragon型のオブジェクトPokkyを代入したとします


このとき


参照変数aを用いて


a.statusDataDisplay()


を実行すると


オーバーライドの働きにより


参照変数aは


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


Pokky.statusDataDisplay()


の管理するメモリ領域にアクセスするのでしたね


つまり


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


Pokky.statusDataDisplay()


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


このとき


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


statusDataDisplay()


の定義

👇

void Dragon::statusDataDisplay() {


cout << name << "\n";

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

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

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

}

👆

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


どうせ


親クラスGameCharacterのメンバ関数

statusDataDisplay()は仮想関数であり


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


statusDataDisplay()によって


オーバーライドされている状態では



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


statusDataDisplay()


の定義


が用いられるのなら


親クラスGameCharacterのメンバ関数


statusDataDisplay()


は何も定義されていなくてもいいとおもいませんか?」


マックス「まあ、確かに・・・」

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

作者を応援しよう!

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

応援したユーザー

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

新規登録で充実の読書を

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

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

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