■
さてさて、ほんの一部の方に関して好評なC++TIPSいってみましょう。今日はわからなくなる人が多い仮想関数について。私がわかっているのかどうか確認の意味も込めて(え?)
仮想関数
#include<iostream.h> class Parents{ public: virtual void func(){ cout << "Parents" << endl; } }; class Child : public Base{ public: virtual void func(){ cout << "Child" << endl; } }; int main(void){ Parents* c = new Child(); c->func(); delete c; return 0; }
実行結果
Parents
非仮想関数(一般関数)
#include<iostream.h> class Parents{ public: void func(){ cout << "Parents" << endl; } }; class Child : public Base{ public: void func(){ cout << "Child" << endl; } }; int main(void){ Parents* c = new Child(); c->func(); delete c; return 0; }
実行結果
Child
どちらのプログラムもmainの最初で基底クラスParentsのポインタに対して派生クラスChildのオブジェクトをnewしています。問題はmainの2行目のnewされたオブジェクトのメンバ関数をポインタで呼び出す部分です。
非仮想関数の場合は呼び出すオブジェクトの型を見てコンパイラが呼び出す関数を決定します。生成されるオブジェクトの型は関係ありません。
一方、仮想関数では実行時に生成されたオブジェクトにしたがって呼び出す関数が決定されるため、Childクラスの関数が呼び出されています。もちろんスコープ演算子を利用して基底クラスの関数を明示的に呼び出すことも可能です。
「ちょっとまって。仮想関数のfuncの呼び出しではどうやって呼び出す関数を決めているの?」
当然の疑問ですね。仮想関数では実行すべき関数のあるクラスをオブジェクトの型では判断できません。そこで、仮想関数に対するポインタを保持するメモリ空間、Virtual-Tableというものが使われています。仮想関数を持つオブジェクトはこのVirtual-Tableに対するポインタを見て、呼び出す関数を決定しています。しかし、当然その分オブジェクトのサイズは大きくなってしまうので注意が必要です。
ふぅ・・・こんなの読む人いるのかなぁ・・・w