virtual function。
仮想関数は、派生クラスで振る舞いをオーバーライドできるメンバ関数である。
非仮想関数と異なり、仮想関数では、コンパイル時に実際のオブジェクト(C++)の型が分からなくても、オーバーライドした振る舞いが保たれる。
つまり、派生クラスのオブジェクトを基底クラスへの左辺値参照(C++)やポインタ(C++)を通して扱っている場合でも、オーバーライドされた仮想関数を呼び出すと、派生クラス側で定義された関数が呼ばれる。
class Base
{
public:
virtual std::string_view getName() const
{
return "Base";
}
};派生クラスが同じシグネチャの関数を定義すると、その関数が基底クラス版をオーバーライドする。
オーバーライドとみなされるには、関数名、引数型、const修飾の有無などが対応している必要がある。
virtualの有無による違い
非virtual:
class Base
{
public:
std::string_view getName() const
{
return "Base";
}
};
class Derived : public Base
{
public:
std::string_view getName() const
{
return "Derived";
}
};
Derived d{};
Base& ref{ d };この場合、ref.getName()はBase::getName()として解決される。
virtual:
class Base
{
public:
virtual std::string_view getName() const
{
return "Base";
}
};
class Derived : public Base
{
public:
std::string_view getName() const
{
return "Derived";
}
};
Derived d{};
Base& ref{ d };この場合、ref.getName()はDerived::getName()を呼び出す。
ただし、ref.Base::getName()のようにスコープ解決演算子で基底クラス版を明示して呼ぶと、仮想呼び出しは行われず、Base::getName()が呼ばれる。
用途
仮想関数を使うと、複数の派生クラスを共通の基底クラスとして扱いながら、それぞれに応じた振る舞いをさせられる。
class Animal
{
public:
virtual std::string_view speak() const
{
return "???";
}
};
class Cat : public Animal
{
public:
std::string_view speak() const
{
return "Meow";
}
};
class Dog : public Animal
{
public:
std::string_view speak() const
{
return "Woof";
}
};Animal&やAnimal*を通してCatやDogを扱っても、speak()は実際の型に応じて呼び分けられる。
純粋仮想関数
派生クラスでのオーバーライドを必須にする仮想関数を、純粋仮想関数 (pure virtual function) という。
純粋仮想関数にするには、宣言の末尾に= 0を書く。
class Polygon
{
public:
virtual int area() const = 0;
};通常の仮想関数は基底クラス側で既定の実装を持てるが、純粋仮想関数は「この操作は存在するが、基底クラスでは実装しないので派生クラスで定義してほしい」という意味を表す。
抽象基底クラス
純粋仮想関数を1つ以上持つクラスは、抽象基底クラス (abstract base class) になる。
抽象基底クラスは、そのままではインスタンス化できない。
class Polygon
{
public:
virtual int area() const = 0;
};このようなクラスは、共通の操作や共通部分を基底クラス側で定義しつつ、具体的な振る舞いは派生クラスに任せたいときに使われる。
インターフェースクラス
状態や実装をほとんど持たず、純粋仮想関数によって操作だけを定義するクラスは、インターフェースクラス (interface class) として使われる。
class Drawable
{
public:
virtual void draw() const = 0;
};このようなクラスは、「何ができるか」を表し、実際にどう行うかは派生クラスが実装する。
抽象基底クラスの中でも、特にインターフェース定義に特化したものと考えられる。