この answer から " C++ではなぜ、どのように仮想関数が遅くなるのですか? "で、作者は以下の点に言及しています:
「vtableからレジスターに正しい関数アドレスを取得します(正しい関数アドレスが格納されるインデックスはコンパイル時に決定されます)。」
このコメントのフォローアップとして、いくつか質問があります。
vtable
内に格納されている関数ポインターの順序は何ですか?それはコンパイラ依存ですか、それともすべてのコンパイラが同じ方法で実装する必要がありますか?
コンパイラーが独自の方法で実装できる場合、COM
のようなバイナリー標準はどのように機能しますか。 COM
は、vtables
ポインターを別のコンパイラーによってコンパイルされたコンポーネントに渡すために、IUnknown
がコンパイラーによって統一された方法で実装されているという前提に依存しています。だよね?
これは実装に依存しますが、かなり予測可能です。特に歴史的に言えば、コンパイラはメンバーを宣言順にレイアウトします。フィールド(インスタンスデータ)の場合、これは、各フィールドにオブジェクトの次のオフセットが割り当てられ(必要に応じて配置が切り上げられた後)、導入された各仮想メソッドに次のvtableスロットが割り当てられることを意味します。 (オーバーライドは、基本クラスでオーバーライドする仮想メソッドと同じvtableスロットを共有します。)
多重継承により、フィールドとvtableのレイアウトが複雑になり、フィールドとvtableのエントリをグループ化するセクションの概念が導入されます。また、生成されたコードは、使用の要求に応じてセクションを切り替える必要があります。
とりわけ、Cは外部関数呼び出しのデフォルトの標準であるため、多くのツールやプログラムはCに依存して、常に行っている予測可能な順序で構造体をレイアウトします。
C++コンパイラーは、最近、特定の構成要素にある程度の寛容を認めていますが、あなたが説明したように、COM(もちろん、Microsoftの標準であり、C++言語の標準ではありません)で使用するには、それらも予測可能である必要があります。
以下も参照してください。
https://stackoverflow.com/questions/9115020/order-of-fields-in-c-c-structs
それはすべて実装次第です。クラスごとのvtable、オブジェクトごとのvtable、またはその他のメカニズムによって仮想関数を実装する必要はありません。
実装はchooseを使用して、特定のオブジェクトレイアウトおよび呼び出し規約(COMなど)と互換性を持たせることができますが、-requiredには対応していません。