コールバック機能へのアクセスを提供するためのインターフェイスをいくつか作成しています。つまり、インターフェイス Aから継承すると、クラスはタイプ 1 のコールバックを使用できます。インターフェイス Bはタイプ 2 を許可します。A と B の両方から継承すると、両方のタイプのコールバックが可能になります。最終的な目的は、クラス A と B がそれらから継承するだけですべての汚れた作業を処理することです。
最初の問題
これは、私が抱えている問題のいくつかを示す小さな例です。
class A
{
public:
static void AFoo( void* inst )
{
((A*)inst)->ABar( );
}
virtual void ABar( void ) = 0;
};
class B
{
public:
static void BFoo( void* inst )
{
((B*)inst)->BBar( );
}
virtual void BBar( void ) = 0;
};
class C : public A, public B
{
public:
void ABar( void ){ cout << "A"; };
void BBar( void ){ cout << "B"; };
};
そして電話をかけることで
C* c_inst = new C( );
void (*AFoo) (void*) = C::AFoo;
void (*BFoo) (void*) = C::BFoo;
AFoo( (void*)c_inst );
BFoo( (void*)c_inst );
出力として「AB」が得られると思います。代わりに「AA」を取得します。派生クラスの順序を逆にすると (A の前に B)、「BB」が生成されます。どうしてこれなの?
2番目の問題
私が使用している実際のインターフェイスはテンプレート化されているため、コードは次のように見えます
template <class T> class A
{
public:
static void AFoo( void* inst )
{
((T*)inst)->ABar( );
}
virtual void ABar( void ) = 0;
};
template <class T> class B
{
public:
static void BFoo( void* inst )
{
((T*)inst)->BBar( );
}
virtual void BBar( void ) = 0;
};
class C : public A<C>, public B<C>
{
public:
void ABar( void ){ cout << "A"; };
void BBar( void ){ cout << "B"; };
};
この理由は、A と B がすべての作業を実行できるようにするためですが、それらの実装には C の知識は必要ありません。
今、
C* c_inst = new C( );
void (*AFoo) (void*) = C::AFoo;
void (*BFoo) (void*) = C::BFoo;
AFoo( (void*)c_inst );
BFoo( (void*)c_inst );
正しい出力「AB」を生成します。
この小さな例はここでは問題なく動作しますが、実際には常に正しく動作するとは限りません。上記の最初の問題の奇妙さと同様に、非常に奇妙なことが起こり始めます。主な問題は、両方の仮想関数 (または静的関数など) が常に C に変換されるとは限らないことです。
たとえば、C::AFoo() を正常に呼び出すことはできますが、常に C::BFoo() を呼び出すとは限りません。これは、A と B から派生する順序に依存するclass C: public A<C>, public B<C>
場合があります。AFoo と BFoo のどちらも機能しないコードを生成することもあれば、どちらか一方または両方が機能class C: public B<C>, public A<C>
するコードを生成することもあります。
クラスはテンプレート化されているので、A と B の仮想関数を削除できます。そうすることで、もちろん C に ABar と BBar が存在する限り、機能するコードが生成されます。それは許容できますが、望ましくありません。むしろ何が問題なのか知りたいです。
上記のコードが奇妙な問題を引き起こす可能性がある理由として、どのようなことが考えられますか?
最初の例では正しくないのに、2 番目の例では正しい出力が生成されるのはなぜですか?