C++におけるvirtual(仮想関数)の使い方を紹介していく

今回は、C++を使う上で重要になるvirtual(仮想関数)について解説していきます。

virtualとは何か?

virtualとは何かを理解するためには、virtualがいつ、どのような時に使われるかを理解した方が早いです。

例えば、以下のコードを見てください。以下のコードは、BaseクラスとChildクラスにfunc()関数が定義されており、Baseクラスのポインタを通してそれぞれのfunc関数を呼び出しています。

#include <iostream>

using namespace std;

class Base {
  public:
  void func(){
    cout << "this is Base" << endl;
  }
};

class Child: public Base {
  public:
  void func(){
    cout << "this is Child" << endl;
  }
};

int main() {
  Base *p;
  Base b;
  Child c;

  p = &b;
  p->func();

  p = &c;
  p->func();

  return 0;
}

上のコードでは、まずBaseクラスのfuncが呼び出されて次にChildクラスのfuncが呼び出されることを期待しますが、結果は以下の様になってしまいます。

this is Base
this is Base

これは、ポインタから関数を呼び出すときには、コンパイラはポインタの型を見て、そのポインタの型のクラスの関数を呼び出そうとするからです。今回の例では、ポインタはBase *p;と定義されているので、p = &c; p->func();としてもBaseクラスのfunc関数を呼び出してしまうのです。

しかし、Baseクラスのfunc関数の先頭にvirtualと書いてあげることで、期待通りの動きをしてくれます。

class Base {
  public:
  virtual void func(){
    cout << "this is Base" << endl;
  }
};
this is Base
this is Child

これはvirtualキーワードを付けることで、virtualの関数がポインタを通して呼ばれた時に、コンパイラはそのポインタ先のオブジェクトを見てどのクラスの関数を呼ぶかを決めるようになるからです。

virtualの使いどころってあるの?

上記の説明を見ると「仮想関数って、あまり使い道なくない?」と感じますが、仮想関数は主にインターフェースを実装するために使われることが多いです。PHPなどではインタフェースは言語がサポートしていますが、C++ではサポートされていないので、仮想関数を使う事で疑似的にインターフェースを実現します。

先ほどのBaseクラスをインターフェースに修正したい場合は、以下の様に書きます。

class Base {
  public:
  virtual void func() = 0;
};

重要なのは、関数をvirtual void func() = 0;のように書くこと。