属性として 1個の int値を持つクラスで、コンストラクタによってこの値が初期化される。
finalizeは オブジェクトを破棄する関数である。
getValueは 値を返す関数である。
ヘッダファイル (header file)
ヘッダファイル Integer.hはつぎのようになる。/* 4.Aug.2012 kei */ #ifndef _Included_Integer #define _Included_Integer typedef struct Integer* Integer; extern Integer new_Integer(int value); extern void Integer_finalize(Integer this); extern int Integer_getValue(Integer this); #endif /* _Included_Integer */多重インクルード防止した上で、型の定義、コンストラクタや関数の宣言を記述する。
型定義については構造体(structure)と typedefで述べているように構造体名と同じ名前を定義し、ポインタも含めてある。
コンストラクタ名は接頭辞として new_を付けてある。命名規約にあるようにクラス名の後ではなく前につけてあるのは newはメンバ関数ではなく C++や Javaの new演算子をエミュレーションしたものだからである。
finalize, getValue関数は引数として自オブジェクトを渡す。この名称は thisを使用しているが、C++から利用する可能性があるならば、thisキーワードとの衝突を防止するために selfなどにするのが良いだろう。(OMT本では selfが使われている)
ソースファイル (source file)
ソースファイル Integer.cを次に示す。/* 4.Aug.2012 kei */ #include <Integer.h> struct Integer { int value; }; #include <malloc.h> Integer new_Integer(int value) { Integer this = (Integer)malloc(sizeof(struct Integer)); if (this != NULL) { this->value = value; } return this; } void Integer_finalize(Integer this) { free(this); } int Integer_getValue(Integer this) { return this->value; }構造体の定義はヘッダファイルではなくソースファイルに記述することにより内部構造を隠蔽することができる。ただこの方法にはクラスを継承させようとしたときに、スーパクラスのインスタンスのサイズを得ることができなくなるデメリットもある。
コンストラクタでは malloc関数を用いてメモリを割り当て、インスタンス変数を初期化し、メモリのアドレスを returnする。
デストラクタでは free関数を用いてメモリを解放する。
getValue関数ではインスタンス変数の値を返却する。
これらから、オブジェクトを生成、利用、破棄がプログラム言語 Cでも実現できることが解かる。thisをコンストラクタで returnしたり、インスタンス関数の引数にすることを明示的に行わなければならない点がオブジェクト指向言語とは異なる。
ビルド (build)
ビルドしてみる。$ make clean install rm -f "libclass1.a"; rm -f *.o; cc -g -I /export/home/kei/c/core/include -I /export/home/kei/c/experimental/include -I /export/home/kei/c/io/include -I /export/home/kei/c/lang/include -I /export/home/kei/ c/message/include -I /export/home/kei/c/unit/include -I /export/home/kei/c/util/ include -I /export/home/kei/c/x11/include -I ../../char/include -I ../../class1/ include -D_REENTRANT -c -o Integer.o Integer.c ar cvrf "libclass1.a" Integer.o ; a - Integer.o mkdir -p /export/home/kei/sub/class1/lib; libclass1.a updated. $この例では静的リンク用にアーカイブファイルを生成している。
いろいろインクルードバスを指定しているのは、sofities projectの各種 sub projectを参照しているものである。今回使用していない io, lang, message, util, x11が含まれているが、これらは別の機会に説明する。
単体テスト (unit test)
Integerクラスを利用する例として単体テストのコード IntegerTest.cを示す。/* 4.Aug.2012 kei */ #define import_lwd_unit_Assert #define import_lwd_unit_CUnit #include <Integer.h> #include <lwd/unit/Assert.h> #include <lwd/unit/CUnit.h> static Integer instance; void setUp() { instance = new_Integer(777); } void tearDown() { Integer_finalize(instance); } void testNewAndFinalize() { /* setUp and tearDown are invoked. */ } void testGetValue() { int value = Integer_getValue(instance); Assert_equalsInt("A value must be 777.", 777, value); } int main(int argc, char** argv) { return CUnit_test(argc, argv); }
sofities projectの test frameworkを使用しているが、フレームワークの使い方は別の機会に説明する。
setUp, tearDownではコンストラクタとデストラクタを呼び出している。
getGetValue関数では getValueを呼び出している。
C++では instance->getValue()のように instanceオブジェクトに対して getValue関数を呼び出すことが理解しやすい記述ができる。Cでは getValue関数に instanceオブジェクトを渡す事しか表現できない。これがオブジェクト指向言語かどうかの違いの1つである。
ついでに、テストの実行の様子も残しておこう。
$ make clean test cc -g -finstrument-functions -I. -I /export/home/kei/c/core/include -I /export/home/ kei/c/experimental/include -I /export/home/kei/c/io/include -I /export/home/kei/c/lang /include -I /export/home/kei/c/message/include -I /export/home/kei/c/unit/include -I /export/home/kei/c/util/include -I /export/home/kei/c/x11/include -I ../../char/ include -I ../../class1/include -c -o IntegerTest.o IntegerTest.c # IntegerTest # [CUnit] ./IntegerTest: testGetValue [CUnit] ./IntegerTest: testNewAndFinalize [SUCCEED] testGetValue in 3(ms) [SUCCEED] testNewAndFinalize in 2(ms) successes: 2 (100.0%) failures: 0 (0.0%) errors: 0 (0.0%) total: 2 in 5(ms) $
撚ってる私のハート
0 件のコメント:
コメントを投稿