仮想メソッドを定義すると、継承ツリーのある時点でそのメソッドをオーバーライドできるようになります。あなたの例のような子孫クラスB
は、何らかの方法でメソッドのロジックを変更できます。仮想メソッドの呼び出しでは、インスタンスを保持する変数の型ではなく、オブジェクト インスタンスの具体的な型に応じて、最も派生したオーバーライドが使用されます。
仮想メソッド テーブル (VMT) を使用して、これらの仮想メソッドと適用されたオーバーライドを追跡します。タイプの変数はA
、クラスから派生した任意のクラスを保持できA
ます。この例では、、、A
またはのB
いずれかです。VMT は、仮想メソッドを呼び出すときに呼び出すオーバーライド メソッドを決定するために使用されます。したがって、あなたの例では、メソッドをオーバーライドします。メソッドを呼び出すと、プログラムは呼び出す正しいメソッドを VMT で検索し、そのメソッドを呼び出します。C
D
B
M
M
各クラスは独自の VMT を定義し、変数は独自の型に関連する VMT を使用します。したがって、型の変数は typeA
に VMT を使用しA
、型B
変数は type に VMT を使用するなど、その変数に格納されているインスタンスの実際の(具体的な) クラスにB
関係なく。オーバーライド解決は、インスタンスの VMT を処理して、実行する正しいメソッドを見つけます。
new
演算子は、同じ名前を持つメソッドを宣言しますが、置換するメソッドの解決チェーンの一部ではありません。新しいメソッドは、独自の VMT エントリのセットを取得する仮想の場合もあれば、標準の非仮想メソッドの場合もあります。
仮想メソッドとオーバーライドを正しく管理するために、コンパイラは処理する情報を持っている必要があります。コンパイル時に変数が何を保持するかを知ることができないため、変数の型だけを使用できます。したがって、 type の変数でメソッドを呼び出すと、 typeに対して実行A
する必要があるのと同じように呼び出しが解決されA
ます。これは、型 A のオブジェクトでランタイム オーバーロードの解決を行うコードを生成することによって行われます。任意の型の変数についても同様です。 . 生成されたコードは宣言された型で機能し、実行時にオーバーライドを解決します。
したがって、 type の変数を宣言してC
から method を呼び出すと、 で定義されているM
名前のメソッドが検索されます。修飾子を使用したため、 で定義された仮想メソッドまたは でのそのオーバーロードへのバックトラッキングが発生することはありません。仮想であるため、VMT を使用して、新しい仮想メソッドの適切なオーバーライドを見つけます。M
C
new
A
B
C.M
したがって、一般に、仮想メソッドが呼び出されたときに実際にどのメソッドが呼び出されるかは、インスタンスの具象型に依存します。new
キーワードにより、変数の型も重要になります。
上記のいくつかは、技術的に正確というよりも記述的に正確です。いつものように、実際の実装は異なる場合があります:)