【C++】仮想関数と純粋仮想関数の違いを徹底解説!宣言方法と使い分けのポイント
C++のオブジェクト指向プログラミングにおいて、ポリモーフィズム(多態性)を実現するために欠かせないのが「仮想関数(Virtual Function)」です。さらに、その特殊な形として「純粋仮想関数(Pure Virtual Function)」が存在します。本記事では、この2つの違いを比較表とサンプルコードを使いながら、初心者にもわかりやすく解説します。
仮想関数と純粋仮想関数の違い一覧
| 仮想関数(Virtual Function) | 純粋仮想関数(Pure Virtual Function) |
|---|---|
| クラス内に実装(定義)を持つ。 | 実装(定義)を持たず、「= 0」として宣言される。 |
宣言方法:virtual 関数名(引数リスト) { ... }; | 宣言方法:virtual 関数名(引数リスト) = 0; |
| 派生クラスでオーバーライドするかどうかは任意。基本クラスだけで完結できる。 | 少なくとも1つでも純粋仮想関数を含むクラスは「抽象クラス」となり、インスタンス化できない。 |
| 必要に応じて基本クラス側でオーバーライドすることも可能。 | 派生クラスは必ず純粋仮想関数をオーバーライドしなければならない。 |
簡単にまとめると、仮想関数は「デフォルトの動作を持つ上書き可能な関数」であり、純粋仮想関数は「派生クラスに実装を強制するための宣言だけの関数」という違いがあります。
仮想関数(virtual function)とは
仮想関数は、基本クラスのポインタや参照を通じて呼び出した際に、実際のオブジェクトの型に応じた関数が実行される仕組みです。virtualキーワードを付けて宣言し、基本クラス内に処理内容(定義)を記述します。派生クラスでオーバーライドするかどうかは自由で、オーバーライドしない場合は基本クラスの実装がそのまま使われます。
サンプルコード
#include <iostream>
using namespace std;
class B {
public:
// 仮想関数:基本クラスに定義がある
virtual void s() {
cout << " In Base \n";
}
};
class D : public B {
public:
void s() { // オーバーライド
cout << "In Derived \n";
}
};
int main(void) {
D d; // クラスDのオブジェクト
B *b = &d; // B* 型のポインタが d を指す
b->s(); // 派生クラスの s() が呼ばれる
return 0;
}実行結果
In Derived
この例では、ポインタの型は B* ですが、実際に指しているオブジェクトは D のインスタンスです。関数が仮想関数として宣言されているため、実行時に派生クラス D::s() が呼び出されます。これが仮想関数による動的ディスパッチ(動的結合)です。
純粋仮想関数(pure virtual function)とは
純粋仮想関数は、宣言の末尾に「= 0」を付けることで定義します。基本クラスには実装を持たず、派生クラスでのオーバーライドを必須とします。また、純粋仮想関数を1つでも持つクラスは「抽象クラス」となり、直接インスタンス化することはできません。インターフェースや設計上の共通契約を定めたい場合に活用されます。
サンプルコード
#include<iostream>
using namespace std;
class B {
public:
virtual void s() = 0; // 純粋仮想関数
};
class D : public B {
public:
void s() { // 必ずオーバーライドが必要
cout << " Virtual Function in Derived class\n";
}
};
int main() {
B *b; // 抽象クラスBはインスタンス化できないため、ポインタのみ使用
D dobj; // クラスDのオブジェクト
b = &dobj; // B* 型のポインタが dobj を指す
b->s(); // 派生クラスの s() が呼ばれる
}実行結果
Virtual Function in Derived class
この例では、基本クラス B の s() は実装を持たない純粋仮想関数です。もし派生クラス D が s() をオーバーライドしていなければ、コンパイルエラーになります。このように、純粋仮想関数は「派生クラスに特定の機能の実装を強制する」ための強力な仕組みです。
まとめ
- 仮想関数:基本クラスに定義があり、派生クラスでのオーバーライドは任意。デフォルトの動作を提供したい場合に使用。
- 純粋仮想関数:「= 0」で宣言され定義を持たない。派生クラスへの実装強制と抽象クラス化を実現し、インターフェース設計に利用される。
両者を正しく使い分けることで、柔軟で拡張性の高いC++プログラムを設計できます。ぜひ実際にコードを書いて、挙動の違いを確認してみてください。
-
C++のnewキーワードとoperator newの違いをわかりやすく解説
C++でオブジェクトを生成する際には、メモリ上にブロックを確保し、さらにコンストラクタを呼び出してそのメモリブロックを初期化するという2段階の処理が必要になります。通常、私たちが new キーワードを使ってオブジェクトを作成すると、この2つの処理が連続して実行されます。一方、operator new はメモリ領域の確保だけを行う関数です。本記事では、この2者の違いを具体例とともに詳しく解説します。newキーワードとはnew 演算子は、ヒープ領域へのメモリ割り当てを要求するための特殊な演算子です。十分なメモリが利用可能な場合にのみ、確保されたメモリへのポインタ変数が返されます。通常の new キ
-
C#におけるメソッドと関数の違い
C#におけるメソッドと関数の違いC#において、「メソッド」と「関数」は本質的に同じものを指します。ただし、C#では一般的に「メソッド」という用語が使われるのが特徴です。これは、C#がオブジェクト指向言語であり、すべてのコードがクラス内に記述されるためです。つまり、メソッドとは「指定されたクラスに属し、そのクラスを通じて動作する関数」のことを意味します。メソッドとは、ひとつのタスクを実行するためにまとめられたステートメント(文)のグループです。すべてのC#プログラムには、Mainという名前のメソッドを持つクラスが少なくとも1つ含まれており、プログラムの実行は必ずこのMainメソッドから開始されま