こんな感じにするのですが。
ClassD_VFTABLEはこれの先頭を指し、ClassC_VFTABLEはこれのClassC_VFPTRの先頭を指す
struct MyClassD_VFTABLE;
struct MyClassD_VFTABLE2;
/* データ部定義 */
struct MyClassD_OBJECT
{
int dummy;
};
struct MyClassD_DATA
{
struct MyClassA_OBJECT MyClassA;
struct MyClassB_OBJECT MyClassB;
struct MyClassD_OBJECT MyClassD;
};
struct MyClassD_DATA2
{
struct MyClassC_OBJECT MyClassC;
};
/* クラス本体定義 */
struct MyClassD
{
struct MyClassD_VFTABLE *call;
struct MyClassD_DATA data;
struct MyClassD_VFTABLE2 *call2;
struct MyClassD_DATA2 data2;
};
/* 仮想関数定義 */
struct MyClassD_VFPTR
{
void (*Func1)( struct MyClassD* this, int arg1 );
};
struct MyClassD_VFTABLE
{
struct MyClassA_VFPTR MyClassA;
struct MyClassB_VFPTR MyClassB;
struct MyClassD_VFPTR MyClassD;
struct MyClassC_VFPTR MyClassC;
};
struct MyClassD_VFTABLE2
{
struct MyClassC_VFPTR MyClassC;
};
/* インスタンス作成用 */
struct MyClassD* MyClassD_new( void );
/* メンバ関数 */
void MyClassD_Release( struct MyClassD *this );
void MyClassD_Func1( struct MyClassD *this, int arg1 );
MyClassDは以下のように使う。
struct MyClassA *myA;
struct MyClassB *myB;
struct MyClassC *myC;
struct MyClassD *myD;
/* インスタンス作成 */
myD = MyClassD_new();
myA = (struct MyClassA*)myD;
myB = (struct MyClassB*)myD;
/* MyClassCへのキャストはこうしなければならない */
myC = (struct MyClassC*)(&myD->call2);
/* メソッド呼び出し */
myB->call->MyClassA.Func1( myB, 1 );
myB->call->MyClassA.Func2( myB, 1 );
myB->call->MyClassB.Func1( myB, 1 );
myA->call->MyClassA.Func1( myA, 2 );
myA->call->MyClassA.Func2( myA, 2 );
myD->call2->MyClassC.Func1( myC, 2 );
myC->call->MyClassC.Func2( myC, 2 );
/* インスタンス破棄 */
myB->call->MyClassA.Release( myB );
この20行目の呼び出しが正常に行われないと、「継承した」などとは言えないのである。
しかし、その制限によって、MyClassCのメソッドは19行目のように、thisポインタとしてMyClassCへキャストしたものを渡さなければならない。
MyClassAのメソッドは、『VFTABLEポインタの直後からMyClassA用のデータが存在する』ことを要求し、同様にMyClassCのメソッドは、『VFTABLEポインタの直後からMyClassC用のデータが存在する』ことを要求するためである。
これに関連し、もう一つ厄介なことがある。MyClassCのメソッドをオーバーライドした場合だよ!!thisポインタを戻さないとMyClassDのデータ領域にアクセスできないのだ!菱形継承をすると、さらにとんでもないことになるし。
マジ、メンドイのである。だからJavaやC#では多重継承というものが無くなったのだろう。