コンテンツにスキップ

最終更新:2026-10-12

C++ virtual 仮想関数

著者について:

個人公式アカウント(WeChat):

wechat

1.仮想関数と実行時多態

対応するコード:emp.cpp

仮想関数の呼び出しは、指しているまたは参照しているオブジェクトの型によって決まり、ポインタや参照自身の型では決まりません。

2.vptrとvtable

vptr_vtable那些事を参照

3.仮想関数におけるデフォルト引数

対応するコード:default_arg.cpp

デフォルト引数は静的バインディングされ、仮想関数は動的バインディングされます。デフォルト引数の使用は、オブジェクトの型ではなく、ポインタや参照自身の型によって決まります。

4.可能か不可能か

(1) 静的関数は仮想関数として宣言できますか?

理由は主に2つの側面にあります:

静的関数は仮想関数として宣言できず、同時にconstやvolatileキーワードで修飾することもできません

staticメンバ関数はどのクラスオブジェクトにもクラスインスタンスにも属さないため、この関数にvirutalを付けても何の意味もありません

仮想関数はvptrとvtableに依存して処理されます。vptrはポインタであり、クラスのコンストラクタの中で生成され、thisポインタでしかアクセスできません。静的メンバ関数にはthisポインタがないため、vptrにアクセスできません。

コード学習:static_error.cpp

(2)コンストラクタは仮想関数にできますか?

コンストラクタは仮想関数として宣言できません。また、inline|explicit以外のキーワードは、コンストラクタに使用することは許されません。

なぜコンストラクタは仮想関数にできないのでしょうか?

仮想関数テーブル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.純粋仮想関数と抽象クラス

純粋仮想関数と抽象クラス那些事を参照

関連トピック

コメント