実開発に使われるC言語 [基礎から裏技まで] (3) Object-Oriented C その1 継承
-
CでもObject-Orientの手法でプログラミングすることができる。実作業の中でもこういった手法を多用されています。実際、CでObject-Orientationのプログラミングの実装方法はいくつがありますが、完全網羅することができませんが、個人の知っているいくつかの実装方法をこれから何Threadをかけて説明したいと思います。
<1>. struct と ポインター変換の力を利用する
まずは基底クラスを定義しましょう。// file ifcs_ooc_base_class.h: 基底クラスの定義 #ifndef IFCS_OOC_BASE_CLASS_H #define IFCS_OOC_BASE_CLASS_H #if defined (__cplusplus) extern "C" { #endif typedef struct IfcsBaseClass_tag { int base; } IfcsBaseClass; #if defined (__cplusplus) } #endif #endif /* IFCS_OOC_BASE_CLASS_H */
続いて、継承関係のサブクラスA, Bを定義しよう。
// ifcs_ooc_class_a.h #ifndef IFCS_OOC_CLASS_A_H #define IFCS_OOC_CLASS_A_H #include "ifcs_ooc_base_class.h" #if defined (__cplusplus) extern "C" { #endif typedef struct IfcsClassA_tag { IfcsBaseClass *p_parent; int a; } IfcsClassA; #if defined (__cplusplus) } #endif #endif /* IFCS_OOC_CLASS_A_H */
// ifcs_ooc_class_b.h #ifndef IFCS_OOC_CLASS_B_H #define IFCS_OOC_CLASS_B_H #include "ifcs_ooc_base_class.h" #if defined (__cplusplus) extern "C" { #endif typedef struct IfcsClassB_tag { IfcsBaseClass *p_parent; float b; } IfcsClassB; #if defined (__cplusplus) } #endif #endif /* IFCS_OOC_CLASS_B_H */
以上で、基底クラスと、その2つのサブクラスの定義は完了した。
で、これらをどう使うべきか?ここで、C言語の武器であるポインターの登場になります。使い方を見る前に、まず、クラスにメソッド(method)を追加しましょう。
// file ifcs_ooc_base_class.h: 基底クラスの定義 #ifndef IFCS_OOC_BASE_CLASS_H #define IFCS_OOC_BASE_CLASS_H #if defined (__cplusplus) extern "C" { #endif typedef struct IfcsBaseClass_tag { int base; void (*initialize)(struct IfcsBaseClass_tag* const self); void (*finalize)(struct IfcsBaseClass_tag* const self); void (*setVal)(struct IfcsBaseClass_tag* const self, int val); int (*getVal)(struct IfcsBaseClass_tag* const self); } IfcsBaseClass; extern IfcsBaseClass* IfcsBaseClass_create(); extern void IfcsBaseClass_destroy( IfcsBaseClass ** const self); #if defined (__cplusplus) } #endif #endif /* IFCS_OOC_BASE_CLASS_H */
// ifcs_ooc_base_class.c 基底クラスの実装 #include <string.h> #include "ifcs_ooc_base_class.h" /** static function declaration*/ static void IfcsBaseClass_initialize(IfcsBaseClass* const self); static void IfcsBaseClass_finalize(IfcsBaseClass* const self); static void IfcsBaseClass_setVal(IfcsBaseClass* const self, int val); static int IfcsBaseClass_getVal(IfcsBaseClass* const self); IfcsBaseClass* IfcsBaseClass_create() { IfcsBaseClass *p_obj_base = NULL; if (NULL != (p_obj_base = (IfcsBaseClass*)malloc(sizeof(IfcsBaseClass)) )) { p_obj_base->initialize = IfcsBaseClass_initialize; p_obj_base->finalize = IfcsBaseClass_finalize; p_obj_base->setVal = IfcsBaseClass_setVal; p_obj_base->getVal = IfcsBaseClass_getVal; } return p_obj_base; } void IfcsBaseClass_destroy( IfcsBaseClass ** const self) { if ((NULL != self) && (NULL != (*self))) { (*self)->finalize(*self); free(*self); *self = NULL; } } /** static function definitions */ static void IfcsBaseClass_initialize(IfcsBaseClass* const self) { if (NULL != self) { self->base = -1; } } static void IfcsBaseClass_finalize(IfcsBaseClass* const self) { if (NULL != self) { memset(self, 0x00, sizeof(IfcsBaseClass)); } } static void IfcsBaseClass_setVal(IfcsBaseClass* const self, int val) { if (NULL != self) { self->base = val; } } static int IfcsBaseClass_getVal(IfcsBaseClass* const self) { if (NULL != self) { return self->base; } return -1; }
続いて、ClassAとClassBに対してもメソッドを追加する。
// ifcs_ooc_class_a.h , クラスAの定義、defineマクロやincludeなどを省略した typedef struct IfcsClassA_tag { IfcsBaseClass *p_parent; int a; void (*initialize)(struct IfcsClassA_tag* const self); void (*finalize)(struct IfcsClassA_tag* const self); void (*setVal)(struct IfcsClassA_tag* const self, int val); int (*getVal)(struct IfcsClassA_tag* const self); } IfcsClassA; extern IfcsClassA* IfcsClassA_create(); extern void IfcsClassA_destroy( IfcsClassA** const self);
// ifcs_ooc_class_a.c // クラスAの実装 #include <string.h> #include "ifcs_ooc_class_a.h" /** static function declaration*/ static void IfcsClassA_initialize(IfcsClassA * const self); static void IfcsClassA_finalize(IfcsClassA * const self); static void IfcsClassA_setVal(IfcsClassA * const self, int val); static int IfcsClassA_getVal(IfcsClassA * const self); IfcsClassA* IfcsClassA_create() { IfcsClassA *p_obj_a= NULL; if (NULL != (p_obj_a= (IfcsClassA*)malloc(sizeof(IfcsClassA)) )) { if (NULL != (p_obj_a->p_parent = IfcsBaseClass_create())) { p_obj_a->initialize = IfcsClassA_initialize; p_obj_a->finalize = IfcsClassA_finalize; p_obj_a->setVal = IfcsClassA_setVal; p_obj_a->getVal = IfcsClassA_getVal; } else { goto L_destroy; } } return p_obj_base; L_destroy: if (NULL != p_obj_a) { free(p_obj_a); p_obj_a = NULL; return p_obj_a; } } void IfcsClassA_destroy( IfcsClassA** const self) { if ((NULL != self) && (NULL != (*self))) { if (NULL != self->p_parent) { IfcsBaseClass_destroy(&self->p_parent); } (*self)->finalize(*self); free(*self); *self = NULL; } } /** static function definitions */ static void IfcsClassA_initialize(IfcsClassA* const self) { if (NULL != self) { if (NULL != self->p_parent) { IfcsBaseClass_initialize(self->p_parent); } self->a = -2; } } static void IfcsClassA_finalize(IfcsClassA* const self) { if (NULL != self) { if (NULL != self->p_parent) { IfcsBaseClass_finalize(self->p_parent); } memset(self, 0x00, sizeof(IfcsClassA)); } } static void IfcsClassA_setVal(IfcsClassA* const self, int val) { if (NULL != self) { self->a = val; } } static int IfcsClassA_getVal(IfcsClassA* const self) { if (NULL != self) { return self->a; } return -2; }
また、ClassBに対しても同じように実装するが、ここで省略していきます。
これですべてを整えた。では、下記のmain関数を見てみましょう。#include <stdio.h> #include "ifcs_ooc_class_a.h" #include "ifcs_ooc_class_b.h" int main(int argc, char* argv[]) { IfcsClassA *p_obj_a = IfcsClassA_create(); IfcsClassB *p_obj_b = IfcsClassB_create(); // 各自のクラスのinitializeを行う p_obj_a->initialize(p_obj_a); p_obj_b->initialize(p_obj_b); // 基底クラスのメソッドを使う p_obj_a->p_parent->setVal(p_obj_a->p_parent, 100); p_obj_b->p_parent->setVal(p_obj_b->p_parent, 1000); printf("class a base value = %d\n", p_obj_a->p_parent->getVal(p_obj_a->p_parent)); printf("class b base value = %d\n", p_obj_b->p_parent->getVal(p_obj_b->p_parent)); // 各自クラスのメソッドを使う p_obj_a->setVal(p_obj_a, 50); p_obj_b->setVal(p_obj_b, 100.0f); printf("class a value = %d\n", p_obj_a->getVal(p_obj_a)); printf("class b value = %f\n", p_obj_b->getVal(p_obj_b)); // 各自クラスのfinalizeを行う p_obj_a->finalize(p_obj_a); p_obj_b->finalize(p_obj_b); IfcsClassA_destroy(&p_obj_a); IfcsClassB_destroy(&p_obj_b); }
これで一応簡単なObject-Orientationの実装ができました。もちろんprotectとか、privateなどのアクセス制限はどうするかとか、基底クラスをもう少し簡単にアクセスできるようにとか、いろいろと問題点があると思いますが、これからはこれをベースにどんどん説明して進化させていきたいと思います。今回は以上になります。少しでもお役に立つと幸いです。