オブジェクト指向がよく分かんない
C言語の勉強をしばらくやってた。
するとだんだん、プログラミングとは、手続きをひとつひとつ書き下していくことのように思えてくる。
そこそこC言語を勉強した後、いま流行っているらしいオブジェクト指向プログラミングの勉強として Java に手を出した。
そしたらオブジェクト指向が全然分からん。
クラスが存在するのは分かる。継承の概念も何となく分かる。でも interface とか必要性が全く分からん。なんのために存在するのよ。
どうもオブジェクト指向は、今までC言語でやっていたような、手続きをずっと積み重ねていくプログラミングとは色々違うらしい。
よく分かんないから、とりあえず後回しにした。
ふと、 Python を使ってみると、文字列の処理がビックリするほど便利だった。
ちょっとオブジェクト指向に興味が湧いた。
オブジェクト指向的な話題は少しずつ触れるようになった。
しばらくたって、オブジェクト指向の「データと手続きをひとまとめにする」という考え方には納得した。
データを、扱う手続き以外からはなるべく隠すということ。
関数やファイルごとのスコープの違いと実用性についてなんとなく分かってきた頃だった。
構造体って結構便利。構造体の型名で変数名を定義するのってなんかクラスっぽい。
最近、C言語のポインタの本を読んだ。
ふとその背表紙を見かけた時、友人が昔読んでいたのを思い出した。
実際、ポインタに自信はなかったし。
そこに、いつ使うのか全く分からなかった共用体の使い方とポリモルフィズムについて書かれていて、いろいろと腑に落ちた。
オブジェクト指向なんとなく便利そう、と思えた。
文字列クラスぽいものを作ってみようと思った。
文字列を保持し、文字数、最初の文字、最後の文字を返す、という三つの手続きを持つ。
もしC言語で作るとしたらこんな感じだろうか。
mystring.h 文字列クラス String を定義するヘッダ
/* mystring.h */ #ifdef __cplusplus extern "C" { #endif #ifndef _mystring_ #define _mystring_ // ここに書いてる関数内で使ってるライブラリ #include <string.h> #include <stdlib.h> // String をまず不完全型として定義 typedef struct String_tag String; // String を実際に定義 struct String_tag { char *str; size_t (*Length)(String *obj); // 文字列の長さを返す char (* LString)(String *obj); // 文字列の最初の文字を返す char (* FString)(String *obj); // 文字列の最後の文字を返す }; // typedef したあとに定義するこの流れ、まだよく分かってない // そもそも正しくない? // 文字列を入れると領域を確保 String *stringInit(const char *s); // String オブジェクトとして確保した文字列のメモリ解放 // あと手続きを使用不可にする void stringFree(String *obj); #endif #ifdef __cplusplus } #endif
string.c 文字列クラス String の手続きを定義するソースファイル
#include <stdio.h> #include <string.h> #include <stdlib.h> #include "mystring.h" // 関数プロトタイプ宣言いくつか static size_t st_length(String *obj); static char st_fString(String *obj); static char st_lString(String *obj); static void *st_finish(String *obj); // 文字列を入れると領域を確保 String *stringInit(const char *s) { // 確保するオブジェクト String *obj; // 渡された文字列のチェック必要? if( 1>=strlen(s) )return NULL; // 構造体分のメモリ確保 obj = malloc( sizeof(String) ); if( NULL == obj )exit(EXIT_FAILURE); // エラーチェック面倒だけど一応やっとく // 関数ポインタの初期化 obj->Length = st_length; obj->LString = st_lString; obj->FString = st_fString; // 文字列分のメモリ確保 obj->str = malloc( sizeof(char)*(strlen(s)+1) ); if( NULL == obj->str )exit(EXIT_FAILURE); // エラーチェック面倒だけど一応やっとく // やっと文字列代入 strncpy(obj->str, s, strlen(s)+1); // 確保成功 return obj; } // 文字列の長さを返す( '\0' は含まない) static size_t st_length(String *obj) { return strlen(obj->str); } // 文字列の最初の文字を返す static char st_fString(String *obj) { return obj->str[0]; } // 文字列の最後の文字を返す static char st_lString(String *obj) { return obj->str[strlen(obj->str)-1]; } // 何を渡されても NULL を返す static void *st_finish(String *obj) { return NULL; } // String オブジェクトとして確保した文字列のメモリ解放 // あと手続きを使用不可にする void stringFree(String *obj) { free(obj->str); obj->str = NULL; // 関数ポインタを NULL で埋めず、 NULL を返す関数で上書き // この代入は戻り値が違うからコンパイル警告が出てしまう・・・ obj->Length = st_finish; obj->LString = st_finish; obj->FString = st_finish; /* // NULL で埋めた関数ポインタにアクセスした時、 // セグメンテーションフォルトで落ちてしまう。 // エラーを返すようにしたいので避けた。 obj->Length = NULL; obj->LString = NULL; obj->FString = NULL; */ }
main.c 実際に String クラスっぽいものを使ってみるソースファイル
#include <stdio.h> #include <stdlib.h> #include "mystring.h" int main(int argc, char *argv[]) { String *hello =stringInit("Hello, world!"); // オブジェクトを初期化 // 初期化エラーの確認いる? if( NULL == hello )exit(EXIT_FAILURE); // 文字列にアクセスと表示 printf("%s\n", hello->str); printf("String Length: %d\n", (int)hello->Length(hello)); printf("First character: %c\n", hello->LString(hello)); printf("Last character: %c\n", hello->FString(hello)); // オブジェクトの手続き呼び出しにいちいち自分自身を引数にするのって面倒くさい /* // オブジェクトの関数ポインタごしだと使えるけど、 // 直接使おうとするとコンパイルエラー。ええ感じ。 st_length(hello); st_fString(hello); st_lString(hello); */ // オブジェクトを解放 stringFree(hello); printf("\nString Free.\n"); /* hello=NULL; // こう NULL で埋めてしまうと、これ以降に hello の手続きにアクセスした時に // セグメンテーションフォルトで落ちてしまう。 // 落ちずにエラーを返させるため NULL で埋めずに生かしとくけど、 // もっと良い方法ないかな・・・。 */ // stringFree 後に手続きを呼び出しても安全(?)にエラーを返すことを確認 printf("String Length: %d\n", (int)hello->Length(hello)); printf("First character: %c\n", hello->LString(hello)); printf("Last character: %c\n", hello->FString(hello)); // 終了。お疲れさまでした。 return 0; }
実行結果。
imadedede$ ./string_c
Hello, world!
String Length: 13
First character: !
Last character: HString Free.
String Length: 0
First character:
Last character:
結論:C言語じゃクラスを書くの面倒。
なので、今はとりあえず C++ の本を読んでる。
いろいろソフトウェアを作ってみたいな。
余談。
この記事書く時はポリフォリズムと憶えていた。でも検索しても Wikipedia がトップに出てこないなんておかしいなーと思っていたら、正解はポリモーフィズム(ポリモルフィズム)みたいだった。
カタカナ語難しい。