Derived
Base
サブオブジェクトがどこにあるかを知るための何らかの方法が必要です。仮想継承では、基本クラスの相対的な場所は、派生クラスの場所に対して固定されていません。完全なオブジェクトのどこにでも配置できます。
ダイヤモンドの継承を含むより典型的な例を考えてみましょう。
struct A
{
int a;
};
struct B1 : virtual A
{
int b1;
};
struct B2 : virtual A
{
int b2;
};
struct C : B1, B2
{
int c;
};
ここでは、B1
とB2
は事実上、から派生しA
ているため、には、サブオブジェクトC
が1つだけあります。A
両方ともB1
、そのサブオブジェクトB2
を見つける方法を知る必要があります(メンバー変数、またはそれらを定義する場合は 他のメンバーにアクセスできるようにするため)。A
a
A
これは、この場合にvtableが使用される目的です。両方ともB1
、サブオブジェクトB2
のオフセットを含むvtableを持ちます。A
上記のダイヤモンド継承の例を実装するためにコンパイラが何を行う可能性があるかを示すために、Visual C ++ 11DeveloperPreviewによって生成された次のクラスレイアウトと仮想テーブルについて考えてみます。
class A size(4):
+---
0 | a
+---
class B1 size(12):
+---
0 | {vbptr}
4 | b1
+---
+--- (virtual base A)
8 | a
+---
class B2 size(12):
+---
0 | {vbptr}
4 | b2
+---
+--- (virtual base A)
8 | a
+---
class C size(24):
+---
| +--- (base class B1)
0 | | {vbptr}
4 | | b1
| +---
| +--- (base class B2)
8 | | {vbptr}
12 | | b2
| +---
16 | c
+---
+--- (virtual base A)
20 | a
+---
および次のvtables:
B1::$vbtable@:
0 | 0
1 | 8 (B1d(B1+0)A)
B2::$vbtable@:
0 | 0
1 | 8 (B2d(B2+0)A)
C::$vbtable@B1@:
0 | 0
1 | 20 (Cd(B1+0)A)
C::$vbtable@B2@:
0 | 0
1 | 12 (Cd(B2+0)A)
オフセットはvtableのアドレスを基準にしていることに注意してください。また、とのサブオブジェクトに対して生成された2つのvtableB1
のB2
場合C
、オフセットが異なることに注意してください。
(これは完全に実装の詳細であることに注意してください。他のコンパイラは仮想関数とベースを異なる方法で実装する場合があります。この例は、それらが実装される1つの方法を示しており、非常に一般的にこの方法で実装されます。)