Cによる継承の実現
継承を実現するためには、親クラスと派生クラスをそれぞれ用意する必要がありますね。今回のサンプルは、親クラスとして前回までの解説で使用してきたObjectClassを、派生クラスとして新たにSubObjectClassを使用することにします。まずは準備として、今まで使用してきたObjectClassを少し整理します。
#ifndef _OBJECTCLASS_H_INCLUDE_ #define _OBJECTCLASS_H_INCLUDE_ /* ObjectClassクラス定義 */ typedef struct _object ObjectClass; struct _object{ void* object_private_data; /* カプセル化データ */ int (*message)(ObjectClass* self); /* メッセージ */ }; /* オブジェクト生成メソッド */ ObjectClass *Object_new(char *name, int status); /* オブジェクト破棄メソッド */ void Object_delete(ObjectClass *obj); #endif
クラス構造体のtypedefによる定義名と、クラス構造体内のカプセル化データ名称を具体的なものに変更しました。これにともなってObjectClass.cの方も修正する必要がありますが、定義名の変更だけなので、ここでは割愛します。
それでは、さっそくSubObjectClassを実装してきましょう。前述した通り、継承とは「既存のクラスの構造をベースに新しいクラスを定義する仕組み」でした。これは、簡単に言ってしまうと、親クラスのクラス定義を持ってきて、そこに新しい機能を追加するということです。コードで書くと、リスト2のようになります。
#ifndef _SUBOBJECTCLASS_H_INCLUDE_ #define _SUBOBJECTCLASS_H_INCLUDE_ #include "ObjectClass.h" /* SubObjectClassクラス定義 */ typedef struct _subobject SubObjectClass; struct _subobject{ void* object_private_data; /* 親クラスのカプセル化データ */ int (*message)(ObjectClass* self); /* 親クラスのメッセージ */ void* subobject_private_data; /* 派生クラスのカプセル化データ */ int (*message2)(SubObjectClass* self); /* 派生クラスのメッセージ */ }; /* サブオブジェクト生成メソッド */ SubObjectClass *SubObject_new(char *name, int status, int type); /* サブオブジェクト破棄メソッド */ void SubObject_delete(SubObjectClass *obj); #endif
親クラスから引き継いだもの以外に、属性(生成メソッドに渡しているtype)とメッセージ(message2)を追加したというコードになっています。ただ、これだと確かに親クラスの構造を受け継ぐことはできますが、結局コードを重複して書いてしまっているので、全然うまいやり方とは言えませんね。これについては、今回はメッセージ部分を#defineで定義してしまって、親クラスと派生クラスとで共有することにしましょう。
… /* ObjectClassの構造定義 */ #define INTERFACE_OF_OBJECT_CLASS \ void* object_private_data; /* カプセル化データ */\ int (*message)(ObjectClass* self); /* メッセージ */ /* ObjectClassクラス定義 */ typedef struct _object ObjectClass; struct _object{ INTERFACE_OF_OBJECT_CLASS }; …
… /* SubObjectClassの構造定義 */ #define INTERFACE_OF_SUBOBJECT_CLASS \ INTERFACE_OF_OBJECT_CLASS /* ObjectClassの継承 */\ void* subobject_private_data; /* カプセル化データ */\ int (*message2)(SubObjectClass* self); /* メッセージ */ /* SubObjectClassクラス定義 */ typedef struct _subobject SubObjectClass; struct _subobject{ INTERFACE_OF_SUBOBJECT_CLASS }; …
これで、なんとか重複部分をなくすことができました。それでは次に進みましょう。
次は、親クラスから派生クラスへカプセル化された領域を受け継げるようにします。カプセル化の実装については、前回の記事を参照していただくとして、今回は簡単に、内部で定義していた属性定義部分を、ヘッダファイルとして独立させ、派生クラスからも参照できるようにします。
/* ObjectClass属性定義 */ struct _objectclass_attribute { char* name; int status; }; /* ObjectClass属性アクセス用マクロ */ #define OBJECTCLASS_ATTRIBUTE(ptr) \ ((struct _objectclass_attribute*)(ptr->object_private_data))
#include "ObjectClass.h" #include "ObjectClass.private.h" #include <stdio.h> #include <stdlib.h> /* メソッドの実装 */ …
あとは、オブジェクトの生成と破棄の実装です。前回までの記事では、オブジェクトの初期化処理や終了処理は、それぞれオブジェクトの生成メソッド、破棄メソッドの中で行っていました。ですが、継承の実現を考えた場合、親クラスの処理やデータに関する初期化処理、終了処理については、親クラス側で行えるようにした方が嬉しいですね。ということで、初期化処理、終了処理をそれぞれ生成メソッドと破棄メソッドから独立させます。
… /* オブジェクト初期化メソッド */ ObjectClass* Object_initialize(ObjectClass* self, char* name, int status) { /* 属性構造体を生成し、属性を初期化 */ self->object_private_data = malloc(sizeof(struct _objectclass_attribute)); OBJECTCLASS_ATTRIBUTE(self)->name = name; OBJECTCLASS_ATTRIBUTE(self)->status = status; self->message = Object_method; return self; } /* オブジェクト終了処理メソッド */ void Object_finalize(ObjectClass* self) { free(OBJECTCLASS_ATTRIBUTE(self)); } /* オブジェクト生成メソッド */ ObjectClass* Object_new(char* name, int status) { ObjectClass* obj = (ObjectClass*)malloc(sizeof(ObjectClass)); return Object_initialize(obj, name, status); } /* オブジェクト破棄メソッド */ void Object_delete(ObjectClass* obj) { Object_finalize(obj); free(obj); }
また、派生クラス側でこれらのメソッドを実行できるように、先ほど作成したヘッダファイル内にメソッドのプロトタイプ宣言を記述することにします。
… /* 初期化・終了処理 */ ObjectClass* Object_initialize(ObjectClass* self, char* name, int status); void Object_finalize(ObjectClass* self);
さて、駆け足になりましたが、これで骨組みは完成しました。以下に、全体像を示します。まずは、親クラスであるObjectClassです。
まずは、クラス定義です。継承を行うための仕掛けとして、クラスの構造を#defineで外だしにしました。
#ifndef _OBJECTCLASS_H_INCLUDE_ #define _OBJECTCLASS_H_INCLUDE_ /* ObjectClassの構造定義 */ #define INTERFACE_OF_OBJECT_CLASS \ void* object_private_data; /* カプセル化データ */\ int (*message)(ObjectClass* self); /* メッセージ */ /* ObjectClassクラス定義 */ typedef struct _object ObjectClass; struct _object{ INTERFACE_OF_OBJECT_CLASS }; /* オブジェクト生成メソッド */ ObjectClass *Object_new(char *name, int status); /* オブジェクト破棄メソッド */ void Object_delete(ObjectClass *obj); #endif
次は、カプセル化された領域を継承するために独立させたObjectClass.private.hです。
/* ObjectClass属性定義 */ struct _objectclass_attribute { char* name; int status; }; /* ObjectClass属性アクセス用マクロ */ #define OBJECTCLASS_ATTRIBUTE(ptr)\ ((struct _objectclass_attribute*)(ptr->object_private_data)) /* 初期化・終了処理 */ ObjectClass* Object_initialize(ObjectClass* self, char* name, int status); void Object_finalize(ObjectClass* self);
最後は、生成/破棄と初期化/終了を別メソッドに分割したObjectClass.cです。
#include "ObjectClass.h" #include "ObjectClass.private.h" #include <stdio.h> #include <stdlib.h> /* メソッドの実装 */ static int Object_method(ObjectClass* self) { printf("%s.status = %d\n", OBJECTCLASS_ATTRIBUTE(self)->name, OBJECTCLASS_ATTRIBUTE(self)->status); return 1; } /* オブジェクト初期化メソッド */ ObjectClass* Object_initialize(ObjectClass* self, char* name, int status) { /* 属性構造体を生成し、属性を初期化 */ self->object_private_data = malloc(sizeof(struct _objectclass_attribute)); OBJECTCLASS_ATTRIBUTE(self)->name = name; OBJECTCLASS_ATTRIBUTE(self)->status = status; self->message = Object_method; return self; } /* オブジェクト終了処理メソッド */ void Object_finalize(ObjectClass* self) { free(OBJECTCLASS_ATTRIBUTE(self)); } /* オブジェクト生成メソッド */ ObjectClass* Object_new(char* name, int status) { ObjectClass* obj = (ObjectClass*)malloc(sizeof(ObjectClass)); return Object_initialize(obj, name, status); } /* オブジェクト破棄メソッド */ void Object_delete(ObjectClass* obj) { Object_finalize(obj); free(obj); }