0

オブジェクトの型が仮想メソッドを持つクラスであっても、コンパイラーがインスタンス化されたオブジェクトにvptrを割り当てる必要がない最適化の可能性があるかどうか疑問に思いました。

たとえば、次のことを考慮してください。

#include <iostream>
struct FooBase
{
  virtual void bar()=0;
};

struct FooDerived : public FooBase
{
  virtual void bar() { std::cout << "FooDerived::bar()\n"; }
};

int main()
{
   FooBase* pFoo = new FooDerived();
   pFoo->bar();

  return 0;
}

この例では、コンパイラはコンパイル時にpFooのタイプを確実に認識しているため、pFooにvptrを使用する必要はありません。コンパイラがvptrの使用を回避できるもっと興味深いケースはありますか?

4

4 に答える 4

4

Building on Andrew Stein's answer, because I think you also want to know when the so-called "runtime overhead of virtual functions" can be avoided. (The overhead is there, but it's tiny, and rarely worth worrying about.)

It's really hard to avoid the space of the vtable pointer, but the pointer itself can be ignored, including in your example. Because pFoo's initialization is in that scope, the compiler knows that pFoo->bar must mean FooDerived::bar, and doesn't need to check the vtable. There are also several caching techniques to avoid multiple vtable lookups, ranging from the simple to the complex.

于 2010-02-25T17:52:20.993 に答える
4

あなたが示した場合でも、どのコンパイラもあなたが提案したことをするのではないかと思います。

非常に単純なプログラムをすべて1つのファイルで使用しています。

Foo.hとFoo.cppにFooBaseとFooDerivedがあり、main.cppにmainがあるとします。Foo.cppをコンパイルするとき、コンパイラはプログラム全体でvtblが必要ないことをどのように認識しますか。main.cppを見ていません。

最終的な決定は、オブジェクトファイルを変更したり、sizeof(FooDerived)のすべての呼び出しを見つけたりするのに遅すぎて複雑な場合にのみ、リンク時に行うことができます。

于 2010-02-25T17:32:17.027 に答える
0

最新のグローバルに最適化されたコンパイラでさえ、依然として「独立した翻訳」の原則に固執しています。この原則によれば、すべての翻訳ユニットは独立してコンパイルされ、最終的なプログラム全体に存在する可能性のある他の翻訳ユニットについての知識はありません。

外部リンケージを使用してクラスを宣言すると、このクラスを他の変換ユニットで使用できます。コンパイラは、クラスがこれらの他の変換ユニットでどのように使用されるかを認識していません。したがって、そのクラスのすべてのインスタンスは、一般に、構築時に仮想テーブルポインタを適切に初期化する必要があると想定する必要があります。

*pFooあなたの例では、スマートコンパイラはオブジェクトの動的タイプがであると判断するかもしれませんFooDerived。これにより、コンパイラはコードを最適化できます。つまり、式FooDerived::barに応答して関数への直接呼び出しを生成しpFoo->bar()ます(仮想テーブルを使用せずに)。ただし、それでも、コンパイラは通常、各FooDerivedオブジェクトの仮想テーブルポインタを適切に初期化する必要があります。

于 2010-02-25T18:05:46.197 に答える
0

コンパイラーは、どのクラスのメソッドが呼び出されるかを知っているときに、仮想関数呼び出しの間接オーバーヘッドを削減することができます。これは、最適化コンパイラが行うポイントツー分析の形式の結果です。それらはポインター変数のデータフローをトレースし、ポインターが1つのタイプのオブジェクトのみを指すことができる場所であればどこでも、正確にそのタイプの呼び出しを生成し、仮想呼び出しセマンティクスを静的に(つまりコンパイル時に)実装できます。

他の多くの人が言っているように、コンパイラがプログラム全体/リンク時間の最適化を行っていない限り(GCCは4.5でそれを取得します)、個別のコンパイルをサポートするために仮想関数のある種のテーブルを生成する必要があります。

于 2010-02-25T18:21:38.080 に答える