最終更新:2026-10-13
C++ 仮想関数¶
作者について:¶
個人公众号(公式アカウント):

1.仮想関数と実行時多態性¶
対応するコード:emp.cpp
仮想関数の呼び出しは、ポインタや参照が指すオブジェクトの型によって決まり、ポインタや参照自身の型によって決まるわけではありません。
2.vptrとvtable¶
3.仮想関数とデフォルト引数¶
対応するコード:default_arg.cpp
デフォルト引数は静的バインディングされ、仮想関数は動的バインディングされます。使用されるデフォルト引数は、オブジェクトの型ではなく、ポインタや参照自身の型によって決まります。
4.できる・できない¶
(1)静的関数を仮想関数として宣言できるか?
主な理由は二つあります:
静的関数は仮想関数として宣言できず、constやvolatileキーワードで修飾することもできません
staticメンバ関数はどのクラスオブジェクトにもクラスインスタンスにも属さないため、この関数にvirtualを付けても何の意味もありません。
仮想関数はvptrとvtableに依存して処理されます。vptrはポインタであり、クラスのコンストラクタ内で生成され、thisポインタからしかアクセスできません。静的メンバ関数にはthisポインタがないため、vptrにアクセスできません。
コード学習:static_error.cpp
(2)コンストラクタは仮想関数にできるか?
コンストラクタは仮想関数として宣言できません。また、inlineとexplicitを除き、コンストラクタに他のキーワードを使うことは許されていません。
なぜコンストラクタは仮想関数になれないのか?
仮想関数テーブルvtableはコンパイル段階で構築されますが、vtableを指すポインタvptrは実行段階でオブジェクトをインスタンス化するときに初めて生成されます。クラスが仮想関数を持つ場合、コンパイラはコンストラクタにvptrを生成するコードを追加します。ここで問題が生じます。もしコンストラクタが仮想なら、vtableにアクセスするためにvptrが必要ですが、その時点ではまだvptrが生成されていません。したがって、コンストラクタは仮想関数になれないのです。
仮想関数を使う理由は、情報が不完全な状況で多態的な動作が必要だからです。一方、コンストラクタはインスタンスを初期化するためのものであり、インスタンスの型は明確でなければなりません。したがって、コンストラクタを仮想関数として宣言する必要はありません。
コード学習:
(3)デストラクタは仮想関数にできるか?
デストラクタは仮想関数として宣言できます。派生クラスを指す基底クラスのポインタを削除する必要がある場合は、デストラクタを仮想関数として宣言すべきです。実際、クラスが他のクラスに継承される可能性が少しでもある限り、仮想デストラクタを宣言すべきです(たとえそのデストラクタが何もしなくても)。
コード学習:
(4)仮想関数はprivateな関数にできるか?
- 基底クラスのポインタが派生クラスのオブジェクトを指す場合、派生クラスのオブジェクトの関数が呼ばれます;
- int main()はBaseクラスのフレンドとして宣言されなければならず、さもないとコンパイルに失敗します。コンパイラのエラー:ptrはprivateな関数にアクセスできません。もちろん、基底クラスをpublic、派生クラスをprivateとして宣言すれば、この問題はなくなります。
コード学習:
(5)仮想関数はinlineにできるか?
通常、クラスのメンバ関数はコンパイラによってインライン化するかどうかが検討されます。しかし、基底クラスのポインタや参照を通じて呼び出される仮想関数は、絶対にインライン化できません。もちろん、実オブジェクトによる仮想関数の呼び出しや静的な呼び出しはインライン化でき、仮想デストラクタの静的呼び出しは必ずインライン展開されます。
- 仮想関数はインライン関数になれます。inlineは仮想関数を修飾できますが、仮想関数が多態性を発揮するときはインライン化できません。
- インライン化はコンパイラに対する提案であるのに対し、仮想関数の多態性は実行時のものであり、コンパイラは実行時にどのコードが呼ばれるかを知ることができません。したがって、仮想関数が多態的に振る舞うとき(実行時)はインライン化できません。
inline virtualがインライン化できる唯一の場合は、コンパイラが呼び出されるオブジェクトがどのクラスに属するかを知っているとき(例えばBase::who())で、これはコンパイラがオブジェクトへのポインタや参照ではなく実際のオブジェクトを持っているときにのみ起こります。
コード学習:
5.RTTIとdynamic_cast¶
RTTI(Run-Time Type Identification、実行時型情報)により、プログラムは基底クラスのポインタや参照を使って、それらが指すオブジェクトの実際の派生クラス型を調べることができます。
オブジェクト指向プログラミングでは、あるオブジェクトを特定の多態的型として使用できるかを実行時に問い合わせたい場合があります。JavaのinstanceofやC#のas・is演算子と同様に、C++は動的キャストのためにdynamic_castを提供しています。CスタイルのキャストやC++のreinterpret_castと比べて、dynamic_castは型安全チェックを提供し、能力問い合わせ(Capability Query)に基づく変換であるため、多態的型間の変換にはdynamic_castを使うことが推奨されます。
コード学習:
6.純粋仮想関数と抽象クラス¶
関連トピック¶
- C++ 純粋仮想関数と抽象クラス | C++那些事
- C++ assert アサーション | C++那些事
- C++ ビットフィールド | C++那些事
- C 言語による C++ 多態の実現 | C++那些事
- C++ const 詳解:定数・const ポインタとポインタ const の違い | C++那些事
- C++ const ファミリー | C++那些事
- C++ 間接参照演算子 * | C++那些事
- C++ 初期化と代入 | C++那些事
- C++ 複数戻り値の書き方 | C++那些事
- C++ mutable キーワード | C++那些事