SHOEISHA iD

※旧SEメンバーシップ会員の方は、同じ登録情報(メールアドレス&パスワード)でログインいただけます

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

Cでわかるオブジェクト指向

【第3回】継承


  • X ポスト
  • このエントリーをはてなブックマークに追加

Cによる継承の実現

 継承を実現するためには、親クラスと派生クラスをそれぞれ用意する必要がありますね。今回のサンプルは、親クラスとして前回までの解説で使用してきたObjectClassを、派生クラスとして新たにSubObjectClassを使用することにします。まずは準備として、今まで使用してきたObjectClassを少し整理します。

リスト1:ObjectClass.h(整理版)
#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のようになります。

リスト2:SubObjectClass.h
#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で定義してしまって、親クラスと派生クラスとで共有することにしましょう。

リスト3:ObjectClass.hの変更
…
/* 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
};
…
リスト4:SubObjectClass.hの変更
…
/* 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
};
…

 これで、なんとか重複部分をなくすことができました。それでは次に進みましょう。

 次は、親クラスから派生クラスへカプセル化された領域を受け継げるようにします。カプセル化の実装については、前回の記事を参照していただくとして、今回は簡単に、内部で定義していた属性定義部分を、ヘッダファイルとして独立させ、派生クラスからも参照できるようにします。

リスト5:ObjectClass.private.h(ヘッダファイルとして独立した属性定義部)
/* ObjectClass属性定義 */
struct _objectclass_attribute {
    char* name;
    int   status;
};
/* ObjectClass属性アクセス用マクロ */
#define OBJECTCLASS_ATTRIBUTE(ptr) \
    ((struct _objectclass_attribute*)(ptr->object_private_data))
リスト6:ObjectClass.cの変更
#include "ObjectClass.h"
#include "ObjectClass.private.h"
#include <stdio.h>
#include <stdlib.h>

/* メソッドの実装 */
…

 あとは、オブジェクトの生成と破棄の実装です。前回までの記事では、オブジェクトの初期化処理や終了処理は、それぞれオブジェクトの生成メソッド、破棄メソッドの中で行っていました。ですが、継承の実現を考えた場合、親クラスの処理やデータに関する初期化処理、終了処理については、親クラス側で行えるようにした方が嬉しいですね。ということで、初期化処理、終了処理をそれぞれ生成メソッドと破棄メソッドから独立させます。

リスト7:ObjectClass.cの変更(その2)
…
/* オブジェクト初期化メソッド */
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);
}

 また、派生クラス側でこれらのメソッドを実行できるように、先ほど作成したヘッダファイル内にメソッドのプロトタイプ宣言を記述することにします。

リスト8:ObjectClass.private.h
…
/* 初期化・終了処理 */
ObjectClass* Object_initialize(ObjectClass* self, char* name, int status);
void Object_finalize(ObjectClass* self);

 さて、駆け足になりましたが、これで骨組みは完成しました。以下に、全体像を示します。まずは、親クラスであるObjectClassです。

 まずは、クラス定義です。継承を行うための仕掛けとして、クラスの構造を#defineで外だしにしました。

リスト9:ObjectClass.h
#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です。

リスト10: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です。

リスト11: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);
}

次のページ
派生クラス

この記事は参考になりましたか?

  • X ポスト
  • このエントリーをはてなブックマークに追加
Cでわかるオブジェクト指向連載記事一覧

もっと読む

この記事の著者

島田 浩二(Ruby札幌)(シマダ コウジ)

1978年生まれ。電気通信大学電気通信学部情報工学科卒業後、メーカ系ソフトウェア会社にて携帯電話の開発業務に従事した後、2006年より札幌にてフリーのプログラマとして活動。2009年7月に株式会社えにしテックを設立し、同社代表取締役に就任。 日本Rubyの会理事、一般社団法人LOCAL理事、Ruby札幌主宰。

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

この記事は参考になりましたか?

この記事をシェア

  • X ポスト
  • このエントリーをはてなブックマークに追加
CodeZine(コードジン)
https://codezine.jp/article/detail/3708 2009/03/17 13:04

おすすめ

アクセスランキング

アクセスランキング

イベント

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

新規会員登録無料のご案内

  • ・全ての過去記事が閲覧できます
  • ・会員限定メルマガを受信できます

メールバックナンバー

アクセスランキング

アクセスランキング