C++において、仮想関数はオブジェクト指向プログラミングの重要な概念です。
この記事では、C++の仮想関数について詳しく解説します。
C++の仮想関数とは何か?
C++の仮想関数とは、オブジェクト指向プログラミングにおいて、ポリモーフィズム(多様性)を実現するための重要な概念です。
通常の関数は、コンパイル時に呼び出す関数が決定されます。しかし、仮想関数は実行時に呼び出す関数が決定されるため、オブジェクト指向プログラミングにおいて非常に重要な役割を果たします。
例えば、以下のような継承関係があった場合を考えてみましょう。
class Animal {
public:
virtual void speak() {
std::cout << "Animal speaks" << std::endl;
}
};
class Dog : public Animal {
public:
void speak() override {
std::cout << "Dog barks" << std::endl;
}
};
class Cat : public Animal {
public:
void speak() override {
std::cout << "Cat meows" << std::endl;
}
};
この場合、Animal
クラスを継承したDog
クラスとCat
クラスでspeak()
メソッドをオーバーライドしています。そして、以下のようにそれぞれのインスタンスを生成してspeak()
メソッドを呼び出してみます。
int main() {
Animal* animal1 = new Dog();
Animal* animal2 = new Cat();
animal1->speak(); // "Dog barks"
animal2->speak(); // "Cat meows"
delete animal1;
delete animal2;
return 0;
}
この結果「Dog barks
とCat meows
という文字列が表示されます。これは、仮想関数によって実行時に適切なメソッドが呼び出されたからです。つまり、ポリモーフィズムが実現されていることがわかります。
仮想関数の基本的な使い方
C++において、仮想関数はポリモーフィズム(多様性)を実現するための重要な概念です。
仮想関数は、基底クラスで定義された関数を派生クラスで再定義することができます。このようにして、同じ名前の関数でも異なる動作をするようになります。
以下は、仮想関数の基本的な使い方の例です。
#include <iostream>
class Animal {
public:
virtual void speak() {
std::cout << "Animal speaks!" << std::endl;
}
};
class Dog : public Animal {
public:
void speak() override {
std::cout << "Woof!" << std::endl;
}
};
int main() {
Animal* animal = new Animal();
animal->speak();
Dog* dog = new Dog();
dog->speak();
Animal* animal2 = new Dog();
animal2->speak();
delete animal;
delete dog;
delete animal2;
}
上記のコードでは、Animal
クラスと Dog
クラスを定義しています。Animal
クラスには speak()
という仮想関数があります。この関数は、Animal speaks!
という文字列を出力します。
一方、Dog
クラスでは speak()
関数をオーバーライドし、Woof!
という文字列を出力するようにしています。
そして、main 関数内で Animal
クラスと Dog
クラスのインスタンスを生成し、それぞれ speak()
関数を呼び出しています。また、animal2
変数に Dog
クラスのインスタンスを代入し、その後も speak()
関数を呼び出しています。
実行結果は以下の通りです。
Animal speaks!
Woof!
Woof!
このように、仮想関数を使用することでポリモーフィズムが実現されており、同じ名前の関数でも異なる動作が可能になっています。
仮想関数の実装方法
C++の仮想関数は、クラス内で定義された関数にvirtualキーワードを付けることで実現できます。
class Base {
public:
virtual void func() {
std::cout << "Base::func()" << std::endl;
}
};
class Derived : public Base {
public:
void func() override {
std::cout << "Derived::func()" << std::endl;
}
};
上記の例では、Base
クラスとDerived
クラスが定義されています。Base
クラスのfunc()
関数にvirtual
キーワードが付けられており、これによってDerived
クラスでオーバーライドすることが可能になっています。
また、Derived
クラスでオーバーライドする場合は、override
キーワードを付けることが推奨されています。これによって、誤ってオーバーライドしていない場合やシグネチャが異なる場合にコンパイルエラーを発生させることが出来ます。
int main() {
Base* b = new Derived();
b->func(); // "Derived::func()" が出力される
}
上記の例では、ポリモーフィズムを利用してBase
型のポインタ変数b
にDerived
型のインスタンスを代入し、そのまま呼び出しています。
この場合、実行時にb
が指すオブジェクトのメソッドが呼び出されるため、Derived::func()
が出力されます。