実開発に使われるC言語 [基礎から裏技まで] (4) Object-Oriented C その2 Privateの実現
-
前回の話の続きになりますけど、今回は、クラスでPrivate領域はCでどうやって実現するのかについて説明させていただきます。
ここでまず、C++の仮ソースを書いておきます。class TestCpp { public: int input_val; void printVal(); private: int count; int calc_value(); }; int TestCpp::calc_value() { int result = this->input_val + this->count; this->count++; return result; } void TestCpp::printVal() { std::cout << "Value is " << this->calc_value() << std::endl; } int main(int argc, char *argv[]) { TestCpp *obj = new TestCpp(); obj->input_val = 1; for (int i = 0; i < 10; ++i) { obj->printVal(); } delete obj; }
ここで、privateの領域のものは、クラスの外部から直接にアクセスすることができないが、Cの場合、structを利用しているので、なかのものすべては直接に外部からアクセスすることができてしまいます。では、privateの領域をCでどう実現するかを今から説明します。
直球ですが、下記のように前回のClassAのソースコードを改修する:
※ 今回に重要な部分を強調するため、直接に関係のない内容を...で省略させていただきます。// ifcs_ooc_class_a.h ClassAの定義、void *p_privateでprivateの部分を隠す typedef struct IfcsClassA_tag { // public: int input_val; void printVal(struct IfcsClassA_tag* const); // private: void *p_private; } IfcsClassA;
// ifcs_ooc_class_a.c の実装、なかでprivateの部分を定義する #include "ifcs_ooc_class_a.h" // <1>. Privateの内容を1つのStruct構造体で格納して、実装を書く typedef struct IfcsClassAPrivate_tag { int count; void calc_value(struct IfcsClassAPrivate_tag* const, int const); } IfcsClassAPrivate; static int calc_value(IfcsClassAPrivate* const p_private, int const origin) { if (NULL != p_private) { int result = p_private->count + origin; p_private->count++; return result; } else { return origin; } } static IfcsClassAPrivate* IfcsClassAPrivate_create() { IfcsClassAPrivate *p_private = NULL; p_private = (IfcsClassAPrivate*)malloc(sizeof(IfcsClassAPrivate)); p_private->calc_value = calc_value; p_private->count = 0; ... return p_private; ... } ... // Privateの部分はここまで // ここからは、ClassAのpublicの部分の内容です。 static void printVal(IfcsClassA * const self); IfcsClassA* IfcsClassA_create() { ... p_obj_a->p_private = IfcsClassAPrivate_create(); ... p_obj_a->printVal = printVal; ... } void IfcsClassA_destroy( IfcsClassA ** const self) { ... IfcsClassAPrivate_destroy(&p_obj_a->p_private); ... } static void IfcsClassA_initialize(IfcsClassA * const self) { ... self->input_val = 0; ... } static void printVal( IfcsClassA * const self) { IfcsClassAPrivate *p_private = NULL; int result = -1; if (NULL != self) { result = self->input_val; if (NULL != self->p_private) { // Cのポインター変換で、private領域を内部でアクセスする p_private = (IfcsClassAPrivate*)self->p_private; result = p_private->calc_value(p_private, result); } } printf("class a value is %d\n", result); }
これで、main関数で下記のように書ける:
int main(int argc, char *argv[]) { int i = 0; IfcsClassA *p_obj_a = IfcsClassA_create(); p_obj_a->input_val = 1; for (; i < 10; ++i) { p_obj_a->printVal(p_obj_a); } p_obj_a->finalize(p_obj_a); IfcsClassA_destroy(&p_obj_a); }
まとめ:private領域を実現するというのは、要するに、privateの領域のものを外部から「直接にアクセス」させないことです。Cの場合は、struct内のものはどうしても外部からアクセスされるのですが、void *ポインタを利用して、
privateの実現部分を.cで全部定義して、あとはポインタの変換機能を利用すれば、外部からのアクセス禁止は実現できるようになります。ちなみに、Cの場合、お客さんにライブラリを渡すときに必要なのは、コンパイルされた.soのライブラリファイル + それを利用するための.hファイルのみで、.cの内部実現はお客さんには完全に見れないので、肝心なアルゴリズムや実装内容を保護する観点からでも、こういった実装もいいかと思います。
また、C++のクラス定義に比べると、隠蔽性はもっと強いかなと思ったりもしますね。 (「void*」 VS 「Privateの明確的なDeclaration」)
次回は、Protectedの実現について説明したいですが、その前に、皆さんは自分でその実装に関して考えてみてください。