2

Given a base class B with a virtual function f, a derived class D and it's own implementation of f here are my scenarios:

  1. B& b = *new D; b.f();
  2. D& d = *new D; d.f();

Does the code in bullet 1 involve fetching f's address from vtable and then a jump? Does the code in listing 2 involve any vtable lookup at all?

I understand that these may be compiler dependent, and perhaps the standard will not specify the implementation details. In that case, I would be grateful if someone who understands how GCC or CLANG handles these cases provides for an explanation.

Thanks.

EDIT: Please cut-paste your assembler output; I am still not sure why in 2nd case there should be any lookup in vtable.

4

5 に答える 5

3

ウィキから:

C++ 標準では、動的ディスパッチをどのように実装する必要があるかを正確に規定していませんが、コンパイラは通常、同じ基本モデルでマイナーなバリエーションを使用します。通常、コンパイラはクラスごとに個別の vtable を作成します。

virtualコンパイラは、次のように、関数を含むすべてのクラスに対して vtable を作成します

仮想関数を使用する (または仮想関数を使用するクラスから派生した) すべてのクラスには、独自の仮想テーブルが与えられます。このテーブルは、コンパイラがコンパイル時に設定する単なる静的配列です。仮想テーブルには、クラスのオブジェクトから呼び出すことができる仮想関数ごとに 1 つのエントリが含まれます。このテーブルの各エントリは、そのクラスがアクセスできる最も派生した関数を指す単なる関数ポインタです。

于 2012-05-15T19:20:13.420 に答える
2

仮想メソッド呼び出しです。どちらの場合も、実行時に vtable ルックアップが存在する必要があります。

コンパイラーは、コンストラクターの呼び出しと関数の呼び出しの間に何かが発生したかどうかを知ることができません。これにより、b と d が変更され、実際の型が別のものに変更された可能性があります。

規格の内容:

2 仮想メンバー関数 vf がクラス Base およびクラス Derived で宣言されている場合、Base から直接的または間接的に派生し、同じ名前、parameter-type-list (8.3.5)、および cv-qualification を持つメンバー関数 vf Base::vf が宣言されているので、Derived::vf も (そう宣言されているかどうかにかかわらず) 仮想であり、Base::vf をオーバーライドします。便宜上、仮想関数はそれ自体をオーバーライドすると言います。次に、適切な形式のクラスでは、そのクラスまたはその直接または間接の基本クラスのいずれかで宣言された仮想関数ごとに、その関数とその関数の他のすべてのオーバーライドをオーバーライドする一意の最終オーバーライドが存在します。メンバー ルックアップ (10.2) の規則は、派生クラスのスコープ内の仮想関数の最終的なオーバーライドを決定するために使用されますが、using 宣言によって導入された名前は無視されます。

7 [ 注: 仮想関数の呼び出しの解釈は、それが呼び出されるオブジェクトの型 (動的型) に依存しますが、非仮想メンバー関数の呼び出しの解釈は、その型にのみ依存します。そのオブジェクトを示すポインターまたは参照 (静的型) (5.2.2)。—終わりのメモ]

これは、それがどのように行われるかを強制するものではありませんが、呼び出しが行われたときにオブジェクトの実際の型で呼び出しを解決する必要があることをかなり明確に述べています。

于 2012-05-15T19:09:13.647 に答える
1

標準的なアプローチは、vtable を使用することです。優れた、または本当に賢いコンパイラだけがそれを最適化します。

答えを知るには、コンパイラのアセンブラ結果を調べてください。ほとんどの場合 (常にではないにしても)、vtable アクセスがあります。

于 2012-05-15T19:11:22.847 に答える
0

どちらの場合も、VTable を検索する必要があります。コンパイル時に、コードはどの関数を呼び出すかわからないためです。

すべての命令はコンパイル時に生成されるため、仮想関数呼び出しは v-table ルックアップに変換されてからジャンプします。

于 2012-05-15T19:14:46.337 に答える
0

f()最初のケースでは、その function を宣言したクラスのオブジェクトを呼び出しているvirtualため、プログラムは vtable ルックアップを実行して正しい派生クラスのオーバーライドを見つける必要がありますf()(投稿の簡単な例で、コンパイラがこれを最適化することを許可しない限り、正確なクラスを知っている)

2 番目のケースでは、 がvirtual としてもD宣言されていない限り、vtable ルックアップはありません。クラス idを知ることで、コンパイラはコンパイル時にどちらを呼び出すかを認識します。f()fDf()

更新 (コメントに基づく): 2 番目のケースは最初のケースと同等です。Dのオーバーライド関数は、Bそれを仮想と宣言することにより仮想でもあるためです (今日も新しいことを学びました:))

于 2012-05-15T19:12:28.270 に答える