UPDATE:20030412
CREATE:20030412

お手軽C言語でオブジェクト指向っぽいことに挑戦

Sat Apr 12 13:16:51 JST 2003

目次

はじめに

[UP]

C言語でオブジェクト指向っぽいことに挑戦してみます。
はじめからオブジェクト指向言語を使えといわないでください(笑)。
これはTECHI Vol.15「リアルタイム/マルチタスクシステムの徹底研究」に
「Cによる抽象化プログラミング-抽象データ型の理由-」という章を読んで、
なるほどと思ったことのまとめです。この章の初出は1996年です。
といっても複雑なことをするのではなく、
「データ+手続き」をひとまとめにして取り扱いやすくするということを行います。
ところで、上記の本に、
僕が、以前からおぼろげながら持っていた「オブジェクト指向」に近いものを感じました。
章のはじめにイラストが描いてあり、そこには「自分のことは自分でやろうそれがおとなだ」と
いうプラカードをもったAbstractDataTypeくんがいます。プログラムの中で役にたってくれるのは、
まさに「自分のことは自分で面倒みれる」存在だと思います。
正式に、教育を受けたわけではなく、本もつまみ食いでくいちらした身で、大きな声でいえないですが、
「オブジェクト指向」をかたる際に、例えば、車を部品にわけたり、哺乳類であるとかというクラス分けを
したところで、プログラムをかえって書けなくなってしまうのではと思ってしまいます。
はじめは「自分のことは自分で面倒みれる」、データと手続きをひとまとめにしたオブジェクトを
めざし、しだいに適用する範囲をひろげ、あ〜でもないこ〜でもないとしているうちに、
オブジェクトを取り扱ううえでの先人の知恵がみえてくるのではないかしらと思います。
でもよくわからないです。
ちなみに、オブジェクト指向を語っている人で、ぐっとくるものを感じさせてくれた人は、
青木淳さんで、
http://www.sra.co.jp/people/aoki/IntroductionToOOAOOD/
http://www.sra.co.jp/people/aoki/SmalltalkOdysseia/
や他の書き物を読みますと、不思議な昂揚感が得られます。青木さんの文章は結構好き嫌いが
分かれるのではないかと思いますが、僕はいさぎよさのようなもの感じてます。
ここで使用される用語簡単に定義します。
データ:データです
手続き:データを取り扱うことです。
操作:手続きを呼び出すことです。
基本的に[データ]は[手続き]を[操作]することで、取り扱われます。

データ,手続きが分離しているとは

[UP]

人間はまちがいを犯すうえに、忘れるという能力をもっています。
C言語でプログラムを、複雑なプログラムをかくということは、データ構造を構造体で定義し
処理をおこなってくれる関数に次から次へと渡していくことだと思います。
この時に、データと関数の距離があればあるほど、それを取り扱うのが面倒になってきます
そうなるとだんだんショートカット的なプログラムを書き始め、プログラムはだんだんと...
ちなみに、僕は万行単位のプログラムを書いたことがありません...
はじめにデータと手続きが分離している場合の処理です。
※例が単純なのと現実的でないですが...
/* データ */
typedef struct {
	char *str;
} MyString_t;


/* 手続き */
void printString(MyString_t *obj)
{
	printf("%s\n", obj->str);
}

/* 操作 */
int main(void)
{
	MyString_t *myString;
	myString = malloc(sizeof(myString));
	myString->str = strdup("abcde");
	
	/* 手続き実行 */
	printString(myString);

	return 0;
}
と、しごく単純なものですが、ここで注目してもらいたいのは、printStringという関数名は、
MyString_tのためにしかプログラム上では使えず、似たようなデータ構造を例えばMyMyString_tを
作成し、printStringと同じ役割の関数を通そうとすると、あたりまえのことですが、まったく別の
名前の関数名を使用して操作しなければなりません。
MyString_tというデータ構造のための、  関数はprintStringForMyString_t
MyMyString_tというデータ構造のための、関数はprintStringForMyMyString_t
とでもしましょうか。
ここで問題なのは、ほぼ同じ手続きをふみたいだけなのに、操作する側で違った
手続き名をおぼえていなければならないことです。
もちろんネーミングルールというものが、この操作を行ううえで助けてくれますが、
そうだとしても、注意を払わなければいけないことにはかわりません。

データ+手続き

[UP]

ここで、C言語でオブジェクト指向っぽいことしてみます。
あたかもデータ+手続きがあわさったようにみせかけて、操作を行います。
typedef void (*PrintString_Func)(void*);

/* データ+手続き */
typedef struct {

	char *str;
	PrintString_Func printString;

} MyString_t;

/* 実際の手続き */
void printString(MyString_t *self)
{
	printf("%s\n", self->str);
}

/* オブジェクト作成 */
MyString_t *createMyString(void)
{
	MyString_t *myString;
	myString = NULL;
	myString = malloc(sizeof(MyString_t));
	if(myString != NULL){
    	myString->printString = (void *)&printString;
	}

	return myString;
}


/* 操作 */
int main(void)
{
	MyString_t *myString;
	myString = createMyString();
	myString->str = strdup("abcde");
	/* この辺オブジェクト指向っぽいでしょ(笑) */
	myString->printString(myString);

	return 0;
}
このようにすると、操作を行う側では、ネーミングの制約を受けずにより直観的に
プログラムできます。もちろん関数ポインタをしようしているだけなので、
実体の関数名は、処理ごとに命名しなければならないですが、操作する側では、
実体の関数名は隠蔽されますので、注意する度合いが減ります。
あと、オブジェクト自身を引数として渡さねばならないという制約を受けます...

商業目的で作成された文書ではありません。

できる限り正しい情報を掲載するようにつとめていますが、明示的、暗黙を問わず保証するものではありません。

正しくない情報が含まれている場合、下記のアドレスに連絡をいただければ幸いです。

掲載されている情報により不利益を被ったとしても、作者は関知いたしません。

nakawaka@abox22.so-net.ne.jp

Valid XHTML 1.0!

this page powerd by smartdoc. thanks smartdoc.