動的ディスパッチ(ポリモーフィズム)がない場合、「メソッド」は単なる暗黙の追加パラメーターを伴う糖質関数です。したがって、ポリモーフィックな振る舞いを持たないクラスのインスタンスはstruct
、コード生成の目的では基本的にC です。
静的型システムでの古典的な動的ディスパッチには、基本的に1つの主要な戦略があります:vtables。すべてのインスタンスは、その型(最も重要なことはvtable)を参照する1つの追加のポインターを取得します。最も重要なのは、メソッドごとに1つの関数ポインターの配列です。(継承チェーン内の)すべての型のメソッドの完全なセットはコンパイル時にわかっているため、連続したインデックス(Nメソッドの場合は0..N)をメソッドに割り当て、関数ポインターを参照してメソッドを呼び出すことができます。このインデックスを使用するvtable(再びインスタンス参照を追加のパラメーターとして渡します)。
より動的なクラスベース言語の場合、通常、クラス自体はファーストクラスのオブジェクトであり、各オブジェクトは代わりにそのクラスオブジェクトへの参照を持ちます。クラスオブジェクトは、言語に依存する方法でメソッドを所有します(Rubyでは、メソッドはオブジェクトモデルのコア部分であり、Pythonではメソッドは小さなラッパーを備えた単なる関数オブジェクトです)。クラスは通常、スーパークラスへの参照も保存し、継承されたメソッドの検索をそれらのクラスに委任して、メソッドを追加および変更するメタプログラミングを支援します。
クラスに基づいていないシステムは他にもたくさんありますが、大きく異なっているため、興味深いデザインの選択肢を1つだけ選択します。たとえば、Haskellの型クラスとRustの特性)、コンパイル中にメソッドの完全なセットは不明です。これを解決するには、特性ごとに vtableを作成し、特性の実装が必要なときにそれらを渡します。つまり、次のようなコード:
void needs_a_trait(SomeTrait &x) { x.method2(1); }
ConcreteType x = ...;
needs_a_trait(x);
これにコンパイルされます:
functionpointer SomeTrait_ConcreteType_vtable[] = { &method1, &method2, ... };
void needs_a_trait(void *x, functionpointer vtable[]) { vtable[1](x, 1); }
ConcreteType x = ...;
needs_a_trait(x, SomeTrait_ConcreteType_vtable);
これは、vtable情報がオブジェクトに埋め込まれていないことも意味します。たとえば、多くの異なるタイプを含むデータ構造に格納されたときに正しく動作する「特性のインスタンス」への参照が必要な場合は、ファットポインターを 作成できます(instance_pointer, trait_vtable)
。これは実際には上記の戦略の一般化です。