■ 本日のお話
- 作る側か? 使う側か?
- オブジェクト指向的書き方は特別なのか?
- オブジェクト指向の何がいいのか?
■ 作る側か? 使う側か?
- オブジェクト指向言語では「ライブラリを作る」か「ライブラリを使う」かで,
難易度に差がある.
- 本勉強会では,ライブラリを作る側を「開発者」,
ライブラリを使う側を「ユーザ」と呼ぶ事にする.
- 本日のお話は,基本的にユーザサイドに限定.
■ オブジェクト指向的書き方は特別なのか?
- 手続き型言語の,2つの記法.
/* 演算子を使う場合 */ /* 手続き的な書き方 */
int a; int a;
a = b; set(b, &a);
a += c; add(c, &a);
- オブジェクト指向言語の典型的な記法.
/* オブジェクト指向の書き方 */
my_int a;
a.set( b );
a.add( c );
- オブジェクト指向の書き方は,「
=
」や「+=
」
を使う場合と語順が同じ.
- 「
=
」や「+=
」といった演算記号だけでなく,
「.文字列(…)
」といった書き方ができるように,言語仕様
を拡張したものが,オブジェクト指向言語.
だから,オブジェクト指向の書き方は,概念的には従来の
演算記号を使った記法と同様であり,
特別視したり,忌み嫌う理由はないのでは?
- 「
.文字列(…)
」をメンバ関数と呼ぶ.
■ オブジェクト指向の何がいいのか?
- コードの中の主人公が明確になる.
- スクリプト言語の長所を導入し,その長所を最大化する.
- ユーザが覚えるべきAPIが最小限で済む.
↓
コード行数の削減・コード可読性の改善・安全性,開発効率アップ
■ スクリプト言語の長所を導入し,その長所を最大化する(1)
- 手続き型言語 C では,型は固定バイト長のものしか扱えなかった.
だから,可変長の文字列や配列では常に,領域の計算・確保・開放
が必要.
/* 文字列 s0 と文字列 s1 を結合 */
char *ptr;
ptr = (char *)malloc(sizeof(char)*(strlen(s0)+strlen(s1)+1));
strcpy(ptr, s0);
strcat(ptr, s1);
printf("文字列 : %s\n",ptr);
:
:
free(ptr); /* ←これを忘れるとメモリリーク */
- オブジェクト指向言語 C++ では,開発者が新しい「型」を作成でき,
これまでユーザが行っていたメモリ管理を「型」に任せる事ができる.
ユーザは処理内容を単純に並べて書くだけ.
/* 文字列 s0 と文字列 s1 を結合 */
tstring str;
str.assign( s0 ); /* 内部バッファを自動的に確保 */
str.append( s1 ); /* 内部バッファの自動的に再確保 */
printf("文字列 : %s\n",str.cstr());
:
:
/* str が消滅すると,内部バッファも自動で開放 */
↓
オブジェクト指向言語をうまく使えば,思考を処理内容に集中でき,
コードの安全性も確保.
■ スクリプト言語の長所を導入し,その長所を最大化する(2)
手続き型言語では,提供される「型」は限定的であった.
開発者が新しい「型」を作成できるのが,オブジェクト指向言語.
- 様々な対象物を,「型」で管理できてしまう.
- 対象物は何でも良い.
文字列,配列,ウィンドゥ,GUI部品,ストリーム,ファイルフォーマット
など.
- 対象物を扱う上での低レベル処理(例:メモリ管理,
通信プロトコル,ヘッダの解析など)は,メンバ関数がやってくれる
(メンバ関数は開発者が実装する).
- ユーザは,提供されるメンバ関数を通じて,
管理されている対象物に間接的にアクセスする.
直接的な(低レベル)アクセスは原則として禁止される.
つまり,レイヤをしっかり分ける.
一般的なスクリプト言語の長所: ちょっとしたモノを作る時,
コードの行数が少なくても,安全なものが作れる
なぜか?
それは,変数で扱える対象物が豊富で,
対象物を扱う上で必要な低レベル処理はエンジンがやってくれて,かつ
ユーザが対象物に低レベルアクセスできないから.
一言で言えば,「変数の実装」.
■ スクリプト言語の長所を導入し,その長所を最大化する(3)
- オブジェクト指向とは,
「スクリプト言語の変数の実装」を様々な対象物に適用するもの.
- 「型」を無限に増やせるオブジェクト指向は,
スクリプト言語の長所を最大化したもの.
言葉の定義
ユーザが覚えるべきAPIが最小限で済む(1)
クラスの継承 (開発者が不統一なAPIを作らないように)
- オブジェクト指向言語では,開発者が既存のクラスを流用して,
新しいクラスを作る事ができる(継承).
- あるクラスを継承して作った新しいクラスを,派生クラスと呼ぶ.
- 継承を使っていないクラスを,基底クラスと呼ぶ.
- 開発者は,
新しい対象物をクラスで管理する時において,様々な派生クラスが作られる事が
予想される場合,「抽象クラス」といって
メンバ関数の仕様だけを策定した基底クラスを作る.
- 開発者は,派生クラスにおいて,基本的には
抽象クラスの持つすべてのメンバ関数を
使えるようにしなければならない.
- 開発者が,
抽象クラスから継承したメンバ関数の引数の仕様を
改変する事は許されない(メンバ関数の追加は可能).
↓
開発者が異なっても,抽象クラスからの継承を使う場合には
メンバ関数の引数がバラバラになる事はない.
↓
ユーザは,抽象クラスのAPIさえ知っていれば,
基本的にはすべての派生クラスが利用可能.
ユーザが覚えるべきAPIが最小限で済む(2)
クラスの継承 (具体例)
LIBSPOOのストリーム入出力APIの設計.
- 抽象クラス(CSTREAMIOクラス)で定義されるメンバ関数.
.open( const char *mode, const char *path )
.close()
.scanf( const char *format, ... )
.printf( const char *format, ... )
.read( void *buf, size_t size )
.write( const void *buf, size_t size )
.getstr( char *buf, size_t size )
.putstr( const char *buf )
.getchr()
.putchr( int ch )
開発者が,抽象クラスを継承するクラス(派生クラス)を作る場合,
上記メンバ関数をすべて使えるようにしなければならない.
- 派生クラス:
・STDSTREAMIO … 標準入出力を扱う
・GZSTREAMIO … gzip圧縮・伸長に対応したストリーム入出力
・BZSTREAMIO … bzip2圧縮・伸長に対応したストリーム入出力
・HTTPSTREAMIO … httpサーバからの入力
・FTPSTREAMIO … ftpサーバから入力・ftpサーバへ出力
などなど
C言語の標準入出力とzlibとの違い
/* stdio.h */ /* zlib.h */
FILE *fp; gzFile fp;
fp = fopen(filename, "r"); fp = gzopen(filename, "r");
fread(buf, 1, size, fp); gzread(fp, buf, size);
fgets(buf, size, fp); gzgets(fp, buf, size);
fclose(fp); gzclose(fp);