C++仮想関数マスター:概念、実例、overrideキーワード
C++仮想関数
このチュートリアルでは、C++仮想関数の目的と実践的な使い方をコード例で解説します。
仮想関数は、派生クラスでオーバーライドされることを期待する基底クラスのメンバ関数です。関数をvirtualで宣言すると、基底クラスポインタや参照経由でアクセスしても、最も派生した実装が呼び出されます。
基底クラスに非仮想メンバがある場合、基底ポインタ経由で呼び出すと、実際のオブジェクト型に関わらず常に基底版が実行されます。以下を考えてみてください:
class Base {
public:
void print() { // 基底実装 }
};
class Derived : public Base {
public:
void print() { // 派生実装 }
};
以下のプログラムでは、Base*がDerivedインスタンスを指していますが、print()は基底版に解決されます:
int main() {
Derived derived1;
Base* base1 = &derived1;
base1->print(); // Base::print()を呼び出し
return 0;
}
ポリモーフィックな振る舞いを実現するには、基底クラスで関数をvirtualで宣言します:
class Base {
public:
virtual void print() { // 基底実装 }
};
仮想関数はC++ポリモーフィズムの中核です。詳細についてはC++ポリモーフィズムガイドを参照してください。
例1: 基本的な仮想関数の使用
#include <iostream>
using namespace std;
class Base {
public:
virtual void print() {
cout << "Base Function" << endl;
}
};
class Derived : public Base {
public:
void print() override {
cout << "Derived Function" << endl;
}
};
int main() {
Derived derived1;
Base* base1 = &derived1;
base1->print(); // Derived Functionを出力
return 0;
}
出力
Derived Function
Base::printがvirtualであるため、基底ポインタ経由の呼び出しでもDerived::printにディスパッチされます。

C++ override識別子
C++11以降、overrideキーワードは、関数が本当に仮想基底メンバをオーバーライドしていることをコンパイラに検証させることで、誤ったオーバーライドを防ぎます。
例:
class Base {
public:
virtual void print() { /* 基底実装 */ }
};
class Derived : public Base {
public:
void print() override { /* 派生実装 */ }
};
クラス外部でオーバーライド関数を定義する場合:
class Derived : public Base {
public:
void print() override; // 宣言
};
void Derived::print() { // 実装
}
overrideの安全な使用
overrideなしでは、タイポやシグネチャの不一致がサイレントに新しい関数を生成し、オーバーライドにならないため、微妙なバグを招きます。overrideを付けるとコンパイラが即座にエラーを出します。よくある落とし穴:
- 名前の不一致: 例:
pint()вместоprint() - 戻り値型の違い: 例:
voidではなくintを返す - パラメータの不一致: 異なるパラメータリスト
- 基底でvirtual欠落: 基底関数がそもそもvirtualでない
仮想関数の実践的活用
Animal基底クラスと派生Dog、Catクラスを考えます。各クラスはコンストラクタで初期化されるtypeメンバを持ちます。
class Animal {
private:
string type;
public:
Animal() : type("Animal") {}
virtual string getType() const { return type; }
};
class Dog : public Animal {
private:
string type;
public:
Dog() : type("Dog") {}
string getType() const override { return type; }
};
class Cat : public Animal {
private:
string type;
public:
Cat() : type("Cat") {}
string getType() const override { return type; }
};
getType()をvirtualにすると、単一のヘルパーで任意の動物を出力できます:
void print(Animal* ani) {
cout << "Animal: " << ani->getType() << endl;
}
main()での使用:
int main() {
Animal* animal1 = new Animal();
Animal* dog1 = new Dog();
Animal* cat1 = new Cat();
print(animal1);
print(dog1);
print(cat1);
delete animal1;
delete dog1;
delete cat1;
return 0;
}
出力
Animal: Animal Animal: Dog Animal: Cat
getType()をvirtualにすることで、各派生クラスにprint()ロジックを複製せず、正しいtypeがアクセスされることを保証できます。
C言語
- C++配列を関数に渡す完全ガイド:1次元・多次元配列の実例付き
- C++クラスとオブジェクト完全ガイド:オブジェクト指向プログラミングの実践
- C++フレンド関数とフレンドクラス:アクセス制御のマスター
- C++クラステンプレート完全ガイド:実践的なジェネリックプログラミング
- C++関数を実例で徹底解説:基本から応用まで
- C言語の関数マスター:構造、宣言、ベストプラクティス
- C++ストレージクラス完全解説:スコープ、ライフタイム、使い方
- C++オーバーロード完全マスター:関数と演算子のオーバーロード
- C++ポリモーフィズム完全理解:仮想関数と純粋仮想関数
- C++データ抽象化完全マスター:インターフェースと実装の分離
- Verilog関数のマスター:効率的なRTL設計のために