2006-06-28
クラスを継承することで、派生クラスは基底クラスの性質を継承することができますが、特定のメンバ関数の振る舞いを、実際のクラス型に応じて変更したいことはよくあります。
例えば、よくある例としては、shapeクラスから派生した、circleクラスとrectangleクラスでは、描画に使用するdrawメンバ関数の振る舞いを変更したいはずです。メンバ関数の振る舞いを変更できなければ、データとして式を持たせたり、別途用意した描画用の関数を、関数へのポインタであるデータメンバに格納するなどの方法を採らざるを得ません。具体的には、こんな感じです。
class shape
{
void (*pfn_draw)();
public:
shape(void (*pfn)()) : pfn_draw(pfn) {}
void draw() { (*pfn_draw)(); }
};
class circle : public shape
{
static void draw_fn();
public:
circle() : shape(&draw_fn) {}
};
確かにこうした方法でも実現はできるのですが、特にこのようなメンバ関数が増えてくると記述が面倒ですし、間違いの元にもなります。効率面でもいろいろと不安が残ります。
そこで、このような問題を解決するための機能がC++には言語機能で備わっています。それが仮想関数です。仮想関数というのは、早い話が、派生クラスで上書き可能な関数のことです。メンバ関数を仮想関数にするには、キーワード virtual を用いて次のように記述します。
class shape
{
public:
virtual void draw() { /* 処理 */ }
};
class circle : public shape
{
public:
void draw() { /* 処理 */ }
};
ここで、circleクラスのdrawメンバ関数を宣言する際には、キーワード virtual は付けても付けなくてもかまいません。
circleクラスのdrawメンバ関数は、shapeクラスのdrawメンバ関数を上書きしたことになります。したがって、オブジェクトがcircle型として生成されたのであれば、例えshape型へのポインタや参照を介してアクセスしたとしても、circle::drawが呼び出されることになります。
このように、C++では、仮想関数を用いることで、メンバ関数の振る舞いを派生クラスごとに変更することが簡単にできます。なお、Javaなどでは、メンバ関数(Javaではメソッド)はデフォルトで仮想関数になるのに対して、C++では、キーワード virtual を明示的に付ける必要があります。これは、仮想関数にすると、若干のオーバーヘッドが発生するからで、効率を優先するC++では、非仮想関数をデフォルトにしているわけです。
Comment