2

仮想関数の単純な構造から複雑な構造まで、あらゆる種類のケースがあります。必要な追加メモリの数を決定する要因は何ですか? 例えば

class A            {virtual void F() {}   };
class B : public A {virtual void F() {}   };

A と B は、仮想関数のないクラスと比較して、どれだけ多くのメモリを必要としますか? 2 つの仮想関数を使用した別の例

class A            {virtual void F() {}   virtual void G() {}   };
class B : public A {virtual void F() {}   virtual void G() {}   };

そして例3

class A            {virtual void F() {};   virtual void G() {}  };
class B : public A {virtual void F() {}                         }; 

[--------------- 興味深い質問を追加 ------------------]

  1. すべての仮想メソッドには仮想メソッド テーブルがありますか? もしそうなら、いくつかの仮想メソッドを持つクラス(オブジェクトではありませんよね?VPTRは静的だと思います。)には、仮想メソッドごとにいくつかのVPTRが必要だと思います。右?

  2. すべての仮想メソッドに対して 1 つのテーブルだけを作成し、1 つのクラスに VPTR を 1 つだけ配置することは可能ですか?

  3. また、直接 VPTR を使用しているため、仮想関数の呼び出し速度は手動 ifs よりも高速である必要があると思います。右?

[ - - - - - - - - テスト - - - - - - - - ]

VS2010 と Intel C++ コンパイラの両方でテストを行い、結果を示しました。

struct A
{
    static int s_i;
    int i;
    virtual void F()    {i+=1;}
    virtual void G()    {i+=2;}
};

struct B
    : public A
{
    int j;
    virtual void F()    {i+=3;}
    virtual void G()    {i+=4;}

    virtual void H()    {i+=5;}
};

struct C
    : public B
{
    virtual void F()    {i+=6;}

    virtual void H()    {i+=7;}
};

TEST(MemoryForVirtualMethod)
{
    CHECK_EQUAL(sizeof(A), 8);
    CHECK_EQUAL(sizeof(B), 12);
    CHECK_EQUAL(sizeof(C), 12);
}

結果から、私の結論は

(1) 仮想関数を持つオブジェクトごとに、1 つの (隠しポインター) VPTR が追加されます。

(2) 各クラスに対して、クラスのすべての仮想メソッドに対して仮想メソッド テーブルが構築されます。

(3) VPTR をオブジェクトに入れるのは、参照されるクラスが動的クラスではない可能性があるため、動的ディスパッチを実装するためです。

(4) 実装は呼び出しが効率的 (手動の ifs よりも高速) ですが、一部のメモリが犠牲になります。

どうもありがとう!

4

1 に答える 1

3

仮想関数は通常、仮想メソッドテーブル(vtable)として実装されるため、 1つO(number of virtual functions)あたりclassと1つあたり1つの追加ポインターを推測していますobject。あなたが望むほど正確ではありませんが、大まかなアイデアを与える必要があります。@AlokSaveが言及しているように、コンパイラ固有であり、推定するのに十分な一貫性がない可能性がある他の多くの微妙な点があります。

通常、コンパイラはクラスごとに個別のvtableを作成します。オブジェクトが作成されると、仮想テーブルポインター、vpointer、またはVPTRと呼ばれるこのvtableへのポインターが、このオブジェクトの非表示メンバーとして追加されます。

出典:ウィキペディア

オーバーライドする関数の数(例のように)は(推測では)無関係です。ただし、呼び出される関数がコンパイル時に安全に決定できる場合は、静的ディスパッチ関数として扱われる可能性G()があります(2番目の関数では)。例)。これもコンパイラ次第です。

コンパイラーは通常、コンパイル時に呼び出しを解決できる場合は常にvtablesの使用を避けます。

最後に、仮想機能のコストは、メモリよりも速度の面で難しい可能性があります。

于 2013-01-23T06:39:08.123 に答える