virtual
なしで継承するvirtualと、メモリ構造は次のようになると想像できます。
class B : A {};

の変数はA、 class の変数の「内側」(すぐ上) にありますB。
with virtual with
を継承する場合virtual、Bへの「ポインタ」だけがあります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

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