>『Java 謎+落とし穴 徹底解明』 3.3 寄り道 - Cで継承を実現してみる
>p.177 - List 3.11 - 3.22 について、 たとえば ExtendedPolyline を追加す
>るとしたらどうするのか、つたないながら考えてみました。
趣旨としては、複数階層の継承を実現するにはどうすればよいか、ということで
よいでしょうか。
>まず、 Shape.c の MethodTableIndex を Shape.h に移動するのはまずいでしょ
>うか。しかし、そうしたとして、あとは以下のファイルを改変または追加しました。
>(なお、 Shape は abstract 相当と理解しております。)
Shape.hは利用者側(たとえばmain.c)でも#includeするヘッダファイルですが、
MethodTableIndexは利用者に晒す必要はないので、Shape.hに移動するのは
よろしくないかと思います。
>/* このへんがとりわけリアリティにかけるのかも…… */
>void initExtendedPolyline(void) {
> ClassDescriptor *cd = getPolylineClassDescriptor();
> void (**mt)() = cd->methodTable;
> memcpy(methodTable, mt, sizeof(mt));
> methodTable[(int)DRAW_INDEX] = drawExtendedPolylineImpl;
>}
まず、このmemcpy()は、ExtendedPolyline独自のmethodTableについて、
いったんそのスーパークラスであるPolylineのmethodTableをコピーしておいて、
その中で特定のメソッドだけをオーバーライドするためのものかと思います。
ただ、上記mtの型は、宣言にあるとおり「voidを返す関数へのポインタへのポインタ」
なので、sizeof(mt)ではポインタのサイズしか取れません。
この形で複数階層の継承を実現するには、スーパークラスのメソッドテーブルに
何個のメソッドがあるのかを、できればコンパイル時に知りたい(メソッドテーブルを
malloc()で確保するなら別ですが、静的に決まる数の要素をmalloc()で確保するのは
避けたい)わけで、そうなると、各クラスのヘッダファイルを、
アプリケーションに公開するもの、自分自身のサブクラスのみに公開するものに
分ける必要があるかと思います。実際、Xt Intrinsicsや
http://kmaebashi.com/programmer/c_yota/inherit.html
こちらのサンプルでは、Polyline.hとPolylineP.hを分けています。
また、クラスディスクリプタを初期化する関数を用意するのは良いとして、
それを、利用者側(main.c)から呼び出させるのは美しくないですよね。
この場合、Objectクラスのクラスディスクリプタに、
・クラスディスクリプタが初期化されているかどうかのフラグ
・スーパークラスのクラスディスクリプタへのポインタ
・クラスディスクリプタを初期化する関数へのポインタ
を持たせてやって、
PolylineやRectangleのような各クラスで、それぞれのクラスディスクリプタへの
ポインタをグローバル変数として公開し、
/* PolylineClassがPolylineのクラスディスクリプタへのポインタ */
Polyline *polyline = new_instance(PolylineClass);
このnew_instanceの中で、
渡されたポインタの指す先のクラスディスクリプタが初期化されていなければ、
そのクラスディスクリプタを初期化する関数を呼び出す。ただしそれを、
親クラスへ遡りながら親クラスから順に行う。
という処理を行えば、クラスディスクリプタの初期化処理を
フレームワーク内で行えるのではないでしょうか。
また、new_instanceの中では、各クラスの「コンストラクタ」を
呼ぶこともできます。
こちらのサンプルでは、
http://kmaebashi.com/programmer/c_yota/inherit.html
クラスディスクリプタを初期化する関数へのポインタclass_initializerを
Coreのクラスディスクリプタに持たせているにもかかわらず、
それを呼び出す処理がどこにも書いてないですね…… orz