クラスに関して④:仮想関数のメリットとおもわれること
昨日、仮想関数のありがたみがよくわからないという話を書いた。
- 親クラスでvirtual void sing()とvoid fly()を用意
- 親クラス型のポインタを宣言
- 親クラス型のポインタに子クラスをインスタンス
- sing()は子クラスのsing()が呼ばれるが、fly()は親クラスのfly()が呼ばれてしまう
じゃあ最初から素直に子クラス型のポインタを宣言して、インスタンスすればいいじゃないか、という内容。実際そうすればうまくいく。
ちょっと例を変えて、main.cppに以下の関数を追加。
void waiting(CBird* bird){ bird->sing(); } void throwing(CBird* bird){ bird->fly(); }
何だかわからないけどとにかくbirdクラスを受け取ったら、それを鳴かせる関数と飛ばせる関数を定義した。 main分で以下の3パターンの動作を確認。
Bird型ポインタにBirdコンストラクタ
CBird* b0;
b0 = new CBird();
waiting(b0);
throwing(b0);
結果は
Birds sing Birds fly
これはOK
Bird型ポインタにCrowコンストラクタ
CBird* b1;
b1 = new CCrow();
waiting(b1);
throwing(b1);
結果は
caw caw Birds fly
これも前回と同じ結果。
Crow型ポインタにCrowコンストラクタ
CCrow* b2;
b2 = new CCrow();
waiting(b2);
throwing(b2);
結果は
caw caw Birds fly
昨日の例ではfly()で"clow flies"となっていたのに、"Birds fly"になってしまった。当然だが、throwing関数の引数をCCrow型ポインタにしてやれば"clow flies"になる。
というわけで
BirdとCrowのようにクラスの中身(メンバ関数とかメンバ変数とか)が全く同じで、そのポインタを親クラスへのポインタと解釈する場合において、仮想関数が威力を発揮するらしい。 今回の例だと箱の中に入った鳥の種類を識別するために、鳴くまで待ってみて「カーカー」鳴いたからこれはカラスだな、みたいなことをやろうとすると仮想関数を使わざるを得ない…ということでとりあえず理解しておく。