問題は、*ppv通常 a void*- に直接割り当てるthisと、単に既存のthisポインターを取得して*ppvその値を与えることです (すべてのポインターを にキャストできるためvoid*)。
これは、単一継承では問題になりません。単一継承では、ベース ポインターがすべてのクラスで常に同じだからです (vtable は派生クラス用に拡張されているため)。
ただし、多重継承の場合、話しているクラスの「ビュー」に応じて、実際には複数の基本ポインターになります。この理由は、多重継承では vtable を拡張することはできないためです。どのブランチについて話しているかによって、複数の vtable が必要になります。
したがって、ポインターをキャストしthisて、コンパイラーが正しいベースポインター (正しい vtable の) を に配置するようにする必要があります*ppv。
単一継承の例を次に示します。
class A {
virtual void fa0();
virtual void fa1();
int a0;
};
class B : public A {
virtual void fb0();
virtual void fb1();
int b0;
};
A の vtable:
[0] fa0
[1] fa1
B の vtable:
[0] fa0
[1] fa1
[2] fb0
[3] fb1
Bvtable があり、それを vtableのように扱う場合、それは機能することに注意してください。Aメンバーのオフセットは、Aまさに期待どおりです。
多重継承を使用した例を次に示します (上記のAとの定義を使用B) (注: 単なる例 - 実装は異なる場合があります)。
class C {
virtual void fc0();
virtual void fc1();
int c0;
};
class D : public B, public C {
virtual void fd0();
virtual void fd1();
int d0;
};
C の vtable:
[0] fc0
[1] fc1
D の vtable:
@A:
[0] fa0
[1] fa1
[2] fb0
[3] fb1
[4] fd0
[5] fd1
@C:
[0] fc0
[1] fc1
[2] fd0
[3] fd1
そして、の実際のメモリ レイアウトD:
[0] @A vtable
[1] a0
[2] b0
[3] @C vtable
[4] c0
[5] d0
Dvtable を として扱うとA機能することに注意してください (これは偶然です。信頼することはできません)。Dただし、 vtableCを呼び出すときc0(コンパイラは vtable のスロット 0 であると想定)として扱うと、突然a0!を呼び出すことになります。
を呼び出すc0とD、コンパイラが行うことは、実際にはthis、 のように見える vtable を持つ偽のポインターを渡しますC。
したがって、関数を呼び出すときは、C関数を呼び出す前に、vtable がオブジェクトDの中央 (vtable で) を指すように調整する必要があります。D@C