問題は、*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
B
vtable があり、それを 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
D
vtable を として扱うとA
機能することに注意してください (これは偶然です。信頼することはできません)。D
ただし、 vtableC
を呼び出すときc0
(コンパイラは vtable のスロット 0 であると想定)として扱うと、突然a0
!を呼び出すことになります。
を呼び出すc0
とD
、コンパイラが行うことは、実際にはthis
、 のように見える vtable を持つ偽のポインターを渡しますC
。
したがって、関数を呼び出すときは、C
関数を呼び出す前に、vtable がオブジェクトD
の中央 (vtable で) を指すように調整する必要があります。D
@C