3
struct A {
int i;
virtual void f() { cout << i; }
A() { i = 1; }
A(int _i) : i(_i) {}
};

struct B : A {
B() : A(2) { f(); }
void f() { cout << i+10; }
};

struct C : B, virtual A {
C() : A() {}
};

Aには仮想メソッドしかないのに、C 構造に 2 つの A::iがあり、 C構造の「仮想テーブル メソッド」へのvptrポインタが 2 つある理由を説明してください。共通ベースからの仮想継承がある場合、共通ベースのインスタンスが 2 つではなく 1 つになることを私は知っています。アドバイスしてください!

4

2 に答える 2

14

virtual
なしで継承するvirtualと、メモリ構造は次のようになると想像できます。

class B : A {};

仮想なしの継承

の変数はA、 class の変数の「内側」(すぐ上) にありますB

with virtual with
を継承する場合virtualBへの「ポインタ」だけがありますA:

class B : virtual A {};

仮想での継承

あなたの例
つまり、あなたの例は次のようになります。

class B : A
class C : B, virtual A

あなたの例

の 1 つのインスタンスのみ の 1 つのインスタンスA のみが必要な場合は、 2 回A使用する必要があります。virtual

class B : virtual A
class C : B, virtual A

A の 1 つのインスタンスのみ

vptrの

g++ によって生成された (で生成された-fdump-class-hierarchy) コードのレイアウトは次のとおりです。

Class C
   size=16 align=4
   base size=8 base align=4
C (0xb7193440) 0
    vptridx=0u vptr=((& C::_ZTV1C) + 12u)
  B (0xb719f078) 0
      primary-for C (0xb7193440)
    A (0xb719a428) 0
        primary-for B (0xb719f078)
  A (0xb719a460) 8 virtual
      vptridx=4u vbaseoffset=-12 vptr=((& C::_ZTV1C) + 28u)

私は vpointer と vtables に関係するものに少し慣れていないので、なぜこれらの v-pointer が正確に存在するのかわかりません。

ただし、仮想メソッドが 1 つしかないというあなたの仮定は間違っていると言えます。からB継承されA、仮想であるため、これを明示的に書き留めなくてもA::f()、派生も自動的に仮想になります。B::f()

編集:

少し掘り下げた後、必要な v ポインターとそうでない v ポインターを覚えていると思います。ただし、以下については保証しません。
以下の表記は、両方の-subobject (と)を区別するために、inの-subobject をC.B.A意味します。ABCAC.B.AC.A

各サブクラスには v-pointer が必要です。ただし*(C.B.A)*(C.B)*Cはすべて同じ場所 (つまり、 と の始まりでもあるクラスのC始まり) を指しているため、1 つの v ポインターを共有できます。ただし、サブクラスへのポインターは別の場所を指すため、別の vpointer はまた、あなたの例ではサブクラスにアクセスできないことにも注意してください (gcc: "警告: あいまいさのため、直接ベース 'A' は 'C' でアクセスできません")。C.BC.B.A*(C.A)C.B.A

少し明確にするために:
次の構造しかない場合

class B : A {};
class C : B {};

のサブクラスへのすべてのポインターがC同じ場所を指すため、必要な vpointer は 1 つだけです。vpointer は、いずれかの vtable を指すかA、どちらがオブジェクトのランタイム タイプであるかを示すだけで済みます。BC

于 2013-04-09T15:46:56.680 に答える
1

B は virtualy を継承していません。

したがって、B から継承された A 構造体と、仮想的に継承された別の構造体があります。

于 2013-04-09T15:25:23.937 に答える