コンストラクタ - みる会図書館


検索対象: 月刊 C MAGAZINE 1992年1月号
13件見つかりました。

1. 月刊 C MAGAZINE 1992年1月号

特集いと G 後述する ) 。クラスにおいて , フレンドと宣 義されているのて、 , 実装の修正は比較的楽 イストラクタと呼ばれる特殊なメンバ関数 言された特別な関数は , メンバ関数て、なく て、あり , また , 実装の細かい部分を知る必 て、ある。 ・インスタンスの構築 . コンストラクタ ても , 非公開メンバにアクセスて、きる。ゆ 要はない ・インスタンスの破棄 . ディストラクタ えに , 抽象データの内部表現となるメンバ 例をあげよう。座標を C の構造体て、表現す を非公開とすることて、 , メンバ関数による れば , これらふたつのメンバ関数により , イン struct point { スタンスの構築と破棄は正しく行われるこ インタフェイスさえしつかりしていれば , 外に見せる必要のないデータを抽象データ とが保証される。 double x, y , の中に隠蔽することがて、きる。 前例て、解説すれば , List 3 のように , コン こて、 , 派生クラスて、のアクセス権を考 ストラクタとディストラクタが呼び出され となる。直交座標表現て、あるが , これを局 えてみよう。クラスの派生て、は , 基底クラ る。 C の構造体を使ったものて、は , メモリ領 座標表現に変えるとどうなるだろうか ? 内 部が丸見えだし , インタフェイス関数も定 域は確保するが , 抽象データとしての構築 スのアクセス権を制御することがて、きる。 と破棄の保証まて、はしてくれない 義されていない。それらの関数を定義する 例をあげよう。 ひとつは , 基底クラスを公開として受け こともて、きるが , C にはそれらを関係づける 継ぐ派生て、ある。 機能はない C 十十て、はデータと演算の関係を強くする struct S ために , 構造体の機能を拡張して , データ クラスて、は , そのメンバへのアクセス権 同様に関数をメンバとして定義することを を制限することがて、きる。クラスのメンバ この場合 , 基底クラスの公開メンバは , 許している。この拡張された構造体はクラ 派生クラスにおいても公開メンバて、ある。 スと呼ばれる。 C 十十のクラスを使えば Lis は , 非公開 (private) , 保護 (protected) , 公 開 (public) と指定することがて、きる。この指 もうひとつは , 基底クラスを非公開とし t 1 のようになる。 定は private とか protected というラベルを使 て引き継ぐ派生て、ある。 これて、 , 関数によるインタフェイスが定 private point { 義された。利用者は実装の細部を知る必要 って行う。 struct S アクセス権については , はない ・アクセス権が非公開であるメン / ヾは , そ このクラスの使用例を List 2 に示す。 のクラスのメンバ関数だけからアクセス この場合 , 基底クラスの公開メンバは , ができる 派生クラスにおいては , 非公開メンバて、あ ・公開メンバは , あらゆるアクセスができ どちらて、あっても , 基底クラスの る 抽象データのインスタンス ( C 十十て、は ob というのが基本となる。唯一の例外は , ク 非公開メンバは , 派生クラスのメンバ関数 やフレンド関数からは直接アクセスて、きな ject とも呼ばれる抽象データの実体 ) 構築と ラスのメンバへのアクセスをとくに許され たフレンド関数の存在て、ある ( 保護について 。なぜなら , 破棄を受け持つのが , コンストラクタとデ い 「アクセス権が非公開であるメンバは , そのクラスのメンバ関数と , そのクラ スからフレンドとみなされた関数 ( フレ ンド ) からだけアクセスできる」 という基本があるからて、ある。派生クラス は , 基底クラスとは違う独立したクラスて、 ある。したがって , 派生クラスて、アクセス するためには , 基底クラスて、は公開としな ければならないことになる。 これて、は , データ隠蔽機構が弱まってし まう。ゆえに , あるクラスて、は , 非公開て、 公開 , 保護 , 非公開 , そして , フレンド public point { コンストラクタと ディストラクタ LiSt 3 1 人っなっ 0 4 一 -0 6 叮ー 8 9 0 point v, *pv; こで v について point::point() が呼ばれる double x : v. xcoord; ← こで *pv について point::point() が呼ばれる pv = new point; ← Pv->P010r(), の ; こで *PV について point::point() が呼ばれる delete pv; vr. move(x + 10 , の : ←ここで v について po i nt : : int ( ) が呼ばれる 特集 C 十十と DJGPP 39

2. 月刊 C MAGAZINE 1992年1月号

のようにしてファイルをオープンしておく 必要がある。 やすい いるものよりもずっとわかりやすく , 使い ストリーム関数やファイル関数て、使われて 合の挙動などを指定するものて、あるが , C の のモードや , ファイルが存在しなかった場 る。これらは , ファイルをオープンする際 cess mode というふたつの引数を必要とす これは , ファイル名のほかに , io mode, ac が , もっとも利用頻度の高いものて、あろう。 m, access mode a) ・ File(const char * filename, iO mode ふたつめのコンストラクタ , 10 mode としては , 次の 5 個の定数が使用 て、きる ー 0 append appendonly readwrite writeonly readonly 読み込み専用 書き込み専用 読み書き両用 追加専用 読み込み , 追加両用 いずれも読んて、字の如して、 , 説明するのも 恥ずかしいくらいて、ある ()o append が読み 込み可能て、あることは , ちゃんとソースに 注釈がついている ) 。 C の記号定数て、行われ ているような変な省略はない もっとも , C におけるネーミングの省略 は , 識別子の長さに厳しい制限があった時 代の名残りて、あって , 別段 C 自体に罪はない のだが ( いまだに関数名や記号定数など , 省 略することを美徳と勘違いしている C プログ ラマは多いが , 最近のコンパイラの識別子 の長さを考えれば , ナンセンスとしかいいよ うがない ) 。 一方の access mode に指定可能な定数は , 以下の四つて、ある。 a createonly 新しくファイルを作る。ファイルがす て、に存在している場合はエラーとなる。 a create 新しくファイルを作る。ファイルがす て、に存在している場合はその内容を破 56 C MAGAZINE 1992 1 棄し , 新たなファイルとして扱う。 a useonly 既存のファイルを利用する。ファイル が存在しない場合はエラーとなる。 既存のファイルを利用する。ファイル が存在しない場合は新たに作成する。 し , create することも ( 当たり前だが ) 不可能 ロノし日 みき両用に設定することはて、きない てある。 なモードは io readonly と io writeonly のみ 御は引数 io mode による。ただし , 指定可能 し , 書き込みも可能て、ある。読み書きの制 字列を入れておいて読み出すこともて、きる のみを用意するものて、ある。バッフアに文 ファイルとの関連づけを行わず , バッファ 最後のコンストラクタは , ディスク上の 利て、ある。 めのオプジェクトを作ったりするのには便 るようにするものて、ある。標準入出力のた し , これを File 型のオプジェクトとして扱え ム関数て、使用される FILE * 型の変数を指定 5 番目のコンストラクタは , C のストリー 使用されるものて、はないだろう。 ode を指定せねばならない。それほど頻繁に 同様 , オープンしたときと矛盾しない io m ものて、ある。第 2 引数には , C の fd 叩 en() と ドル ) を指定して , インスタンスを生成する ンされているファイル記述子 ( ファイルハン 第 4 のコンストラクタは , すて、にオープ かもしれない こちらに慣れている人には扱いやすい もわかりやすいとはいいがたいものなのだ 、、 w クなどの文字を組み合わせた , お世辞に る。 fopen ( ) て、のアクセスモードは , 、、ドや などと同様の文字列によって行うものて、あ は , アクセスモードの指定を , C の fopen() ar* m) FiIe (const char * filename, const ch 第 3 のコンストラクタ 定義されている。 io mode と access mode は , Fmodes. h て、 て、ある。「データがたいした量て、はないの て、 , テンボラリファイルを使うのは懸命て、 ない」ような場合に , ファイルとの入出力を 前提としているクラスを「騙す」ような使い 方もて、きる。 これらのコンストラクタと対応する叩 en 関数が 4 種類用意されている。 List 9 て、は , 28 行目から 31 行目にあたるが , とくに説明 の必要はないて、あろう。それぞれ同じ引数 をもつコンストラクタの解説を参照してい ただきたい ( すて、にお察しの方もあろうかと 思うが , コンストラクタ達は , 内部て、それぞ れの open ( ) メソッドを使用している ) 。 さて , 次は FiIe クラスの入出力用メソッド をひととおり眺めてみたい ますは出力用のメソッドて、あるが , FiIe ク ラスて、は 4 種類が用意されている。 1 文字の出力を行うのが , 69 行目の put ( c har c) : て、あり , 文字列を出力するのが , 75 行目の put(constchar* s) ; て、ある。そ れぞれ , C のストリーム関数 fputc, fputs に 相当する。 このように , C 十十て、は , 同じ関数名て、あ っても , 引数の数や型に違いがあれば , 別 の関数としてきちんと識別される。 C におい てプログラマは , 関数名の衝突を避けるた めにたいへんな努力を要求され , それが C に おける難解な関数名のひとつの要因て、もあ った そういう無意味な努力は , C 十十において は大分解消されている ( なくなったわけて、は ない。クラス名の衝突には気を配らねばな らないし , 関数名以上に衝突の材科となり 得る記号定数については , いまだに野放し て、あるからだ ) 。 バイトサイズて、の出力を行うのが write ( v oid* x, int sz, int n) ; て、ある。これは , C の fwrite に相当する関数て、あり , 第 1 引数 ファイルとの入出力

3. 月刊 C MAGAZINE 1992年1月号

特集いと G —finline—functions' 簡単な関数を呼び出し側関数上にインライン 展開する。コンパイラは関数をインライン展開 の対象とするかどうかヒューリスティックに決 める。 -fno-inline は , どの関数も , たとえ inlin e と宣言されていても , インライン展開しない。 ある関数への呼び出しがすべてインライン展 開され , かっその関数が ' static ' 宣言されている 場合には通常その関数のアセンプラコードは出 -fal ト virtual' 力されなくなる。 C 十十て、は , 関数を inline と旦 を当 :. -fal ト virtual オプションが指定されたら , 「メソッ 言すれば , その関数暗黙のうちに static 宣言とな ド呼び出し」演算子が定義されたクラスと同じクラス : て、宣言された ( コストラクタ関数と new と delete メン : バ演算子を除いた ) すべてのメンバ関数は , そのクラ -fdefault-inline' 三ス vtable ( 仮想関数テープル ) に登録される。事実上 , このオプションが有効て、あれば , クラススコ これらのメソッドは「暗黙の仮想関数」ということに ープの内側て、定義されたメンバ関数は , デフォ 三なる。 ルトて、 inline とコンパイルされる , すなわち , メ このことは , これらメソッド呼び出しのすべてが ンバ関数名の前に inline をつける必要はない。多 三 vtable を使って呼び出されるという意味てはない。対 数の要望により , このオプションは , 現在デ、フ 三象となる仮想関数を直接呼び出せるという状況もし ォルトになった ( * 6 ) 。 GNU C 十十て、 , これらメ こうした場合 , それらは直接呼び出さ : ばしばある。 ンバ関数をインライン展開しないようにするに 三れる。 は , -fno-default-inline を指定すればよい operator- > ( ) ( ) を宣言しているクラスのメソッ ドを一 fal トⅵ rtual を使って暗黙の仮想関数とするなら 三ば , そのようなクラスから派生したすべてのクラス : のメソッドすべて ( コンストラクタ以外の ) にまて、そ : の影響がおよぶ。 このオプションは , コンパイラがコンストラクタ 三を取り除くことがてきるならば , それがてきるよう : にする。このフラグなしては , GNU C 十十と cfron 三 t はどちらも List 5 のコードに対して同じコードを生 : 成する。 この違いに気をつけてほしい。 -felide-constructo 三 rs が指定されれば , GNUC 十十は , 一時変数を使わ : ず fo 。 ( ) の呼び出しによって Y を直接初期化する。 * 6 AT&T C 十十ては , クラススコープて、宣言され たメンバ関数は , デフォル トて inline てある。 GNU C 十十の古いノヾーションて は , それらのメンバ関数は , この -fdefault-inline が明 示的に指定されるか , 明示 的に inline と宣言されない 限り inline 展開されなかっ * 7 ARM て、は , ANSI C と の移植性を考えるのて、あれ ば , 引数なしの関数の宣言 は foo(void) を書くべきだと 述べられている。 * 8 C 十十 2.0 以降ては , クラスごとに new と delete 演算子を定義てきるように なった。その以前ては , thi s ポインタに値を直接代入す ることて , クラスごとに記 憶を管理するという方法が 使われていた。 -fstrict-prototype' C 十十て、は , 宣言 intfoo() : は f00 は引数を持たな いという意味てある。 ANSI C て、は , これは int f00 (void) : と宣言される。フラグ -fno-strict-prototyp e をつければ , 引数をつけずに関数を宣言すること は , 引数リストが型を持たないと宣言することにな る。すなわち , int f00 ( ) : は int f00 (... ) などと等価・ て、ある ( * 7 ) 。 -fthis-is-variable' C 十 + に利用者定義の記憶管理機構を追加すること : は , this への代入を時代遅れにしてしまった ( * 8 ) 。そ : れゆえ , GNU C 十十てはデフォルトて、 this の型を , : classX のメンバ関数中て、 X * const として扱う。言 い換えれば , メンバ関数中て、は this への代入は許され -felide-constructors' List A foo(); A x (foo()); 〃 x は f00 で初期化される。 こではコンストラクタは呼び出されない A y = foo(); 〃 f00 ( ) の返り値は一時 ( 内部 ) 変数に収められ , y は一時変数によって初期化される 1 ↓りな ^ 0 4 -0 特集 C 十十と DJGPP 49

4. 月刊 C MAGAZINE 1992年1月号

なのて、 , ここて、は , 上記のキーワードの中 ュメント標準をかろうじて満たしているあ 一般に抽象データというと , プログラム から , C 十十の特徴的な点に焦点をしばり解 たりて、ある ( ただし , GNU C 十十は ARM を の対象となる情報から必要なものだけを抽 説する。 出して , 計算機上て、うまく表現したものを 完全に実装してはいない ) 。 ・抽象データとクラス 指す。たとえば , 「データ構造とアルゴリズ GNUC 十十の実装は , C 十十の進化を追 ・公開 , 保護 , 非公開 , フレンド ム」の話に現れるリスト構造 , ディクショナ うように ( そして , 時には新しい機能を試験 ・コンストラクタとディストラクタ リとか , 住所録プログラムて、の各人のレコ 的に付け加え ) 進化を続けてきた。 Ver. 1. ・型変換演算子 39 は , 多重継承や新しいリンケージ機構を ード表現とかを考えてもらえれば , それほ なおここて、は , ある程度の C の知識を前提 持っため , おおむね C 十十 2.0 相当て、ある。 ど間違っていないだろう。 しかし実装方法が異なる GNUC 十十て、は , にしている。 そして , 抽象データを使ったプログラム ほかの C 十十コンパイラ同様 , ドキュメント とは , プログラムの一部を抽象データとし に書かれていない細かな部分て、 A T&T てまとめることだと考えられる。それには , C 十十と非互換て、ある。 ・データ表現と実装は , 抽象データの中に そして GNUC 十十は , まだ未完成て、ある 隠してしまい , C 十十とクラスとは非常に関係深い。 C 十十 ( 現在テスト ) 。それゆえ , たとえば InterV ・抽象データをインタフェイス ( 関数群 ) に の前身て、ある C with CIasses がそうて、あっ iews のような C 十十 2.0 て、書かれたプログラ よって定義する たように , C 十十の設計は C にクラス機能を ムはそのままて、はコンパイルて、きず , 比較 ことを行わなければならない。そして , 抽 付加することから始まった。クラスは「抽象 的多くの修正を要求されることになる。つ 象データを組み立てて , プログラムを作成 データ」を実現するための機能て、ある。 まり GNU C 十十は , まだそれほど完成され する。抽象データは , インタフェイスて、定 た C 十十て、はないということて、ある ( 新しい GNU C 2.0 て、は C 十十の機能も取り込まれ ることになっているのて、 , その完成度に期 待しよう ) 。 GNU C 十十 (djgpp) を使うにあ . たって , このことを一応念頭に置いてほしい 抽象データとクラス List 1 struct point { public: point(void) ; point(void); double xcoord(void) ; double ycoord(void); double theta(void) ; double r(void); void move(double x, double y) ; void polar(double r, double theta) : private: 、よりっ 0 -4- 戸 0 6 ゥー 8 0 》 0 、 1 り 0 っ 0 1 人 1 人 1 よ 1 よ C 十十のポイント 初めに C 十十のキーワードをあげてみよ ・抽象データとクラス ・公開 , 保護 , 非公開 , フレンド ・コンストラクタとディストラクタ ・演算子多重定義 ・型変換演算子 ・継承と仮想関数 ・型検査と関数名多重定義 ・インライン関数 これらの話題は , よく C 十十の記事などて、 取りあげられていて , それほど目新しさは ないと思う。そして限られた誌面て、 , 体系 的に C 十十の仕様や機能を解説するのは困難 38 C MAGAZINE 1992 1 LlSt 1 人ワっ 0 4- 一 -0 6 叮ー 8 9 0 point v, *pv; double x V. xcoord; pv ニ new point; pv->polor(), の : delete pv; vr. move(x + 10 , の ;

5. 月刊 C MAGAZINE 1992年1月号

てくれない住民もいるかもしれない なかなか警官稼業もキビしいなあ・・・・・」 筆者 , しばし空想にふける。 ・・と , ゲームのコンセプトは ' , ういうも のて、す。細かい点は , おいおい煮詰めてい Fig. 1 住民クラスの情報と外部インタフェイス ・情報の候補 自分の住所これは必ずしも必要ではないかも・・・・・・多分いらない 「泥棒を目撃した」だけでなく , 「何分前か」を警官に 時計 教えてあげるのが親切というもの 泥棒を見た時刻を覚えておくメモが必要か ? メモ 「 , 泥朝と警官 何をクラスにするか これカ℃言語ならば , たとえば「住民の配 列があって , メインルーチンて、それをいじ くって・・ 。住民にはいくつか種類がある から , 識別するフラグを持たせて , switch 文て、その都度切り分けて・・・・司となるわけて、 すが , このような実装は , よほどモジュー ル化を意識していないと , 理解不能なスパ ゲッティコードになりがちて、す。そうなっ てしまったら最後。「デバッグするより , 最 初から書き直したほうが早いんじゃないか」 なんてことは , 読者の多くが経験してきた スにまとめておいたほうが , 扱いが楽にな 個性といっても , 別に難しいことて、はあ ことだと思います。 りそうて、す。 りません。警官の聞き込みに対するリアク このゲームには , 警官・泥棒・住民とい ションの差というだけのことて、す。いちば った登場人物が出てきますが , ん簡単なのは , この Resident クラスを基本 こいつらは それぞれクラス化するのがよさそうて、す。 「住民」は , このゲームの重要なポイント クラスとしていくつかの派生クラスを作り , この連載て、も前に述べたことがありますが , になる大事な要素て、す。住民が警官の聞き それぞれのクラスて、 Resident : : ask ( ) を クラスというものは , 好むと好まざるとに 込みに対してリアクションを起こし , それ オーバライドすることて、す (List 2 ) 。 かかわらず , わりと簡単に「擬人化」て、きて が中心となってゲームが運ばれていくのが 基本クラス Resident のコンストラクタに しまいます。その作用を積極的に活用すれ 前提になっているのて、すから。 は引数がありませんから , それぞれの派生 ば , わかりやすいコードが書けるかもしれ 住民クラスには , どのような情報が必要 クラスにおいて , 明示的に基本クラスのコ ません。 か。外部に対してどのようなインタフェイ ンストラクタを呼び出す必要はありません。 また「たくさんの住民」は , 全員バラバラ スを持つべきか。まずはそこから攻めてみ それぞれのクラスの ask ( ) を List 3 のよう の個性を持っていたほうがおもしろいて、し に実装します。 よう。さりとて , 現実問題そうもいかない Fig. 1 をまとめて , クラスにしてみましよ これら NormaI, Lyer, AntiP01ice といっ のて、 , 大別して 3 種類の住民がいることに た住民を町にバラまくわけて、すが , いった します。この程度て、あれば , 継承を使って んバラまいてしまえば , もう住民の種別に 住民たち 表現するとすっきりいくてしよう。 気を払う必要はいっさいありません。すべ そのほかに , 隠れた登場人物 ( ? ) として , 実際にゲームに登場する住民は , 「嘘つき」 ての住民は等しく Resident だとみなして , 町そのものがあります。町を擬人化して考 「警察嫌い」などの個性づけが施されなくて 統一的に扱うことがて、きます。仮に , 住民 はいけません。 えるかどうかは別としても , 機能的にクラ の種類をもっと増やしたくなった場合にも , ・インタフェイスの候補 コンストラクタ / デストラクタ 言うに及ばず 聞き込みに対するリアクション これがないと話にならない List 住民クラスの情報と外部インタフェイス 1 : class Resident ( / / 住民クラス 2 : protected: / / 腕時計 : CIock クラスをメンバとして持つ 3 : CIock watch; 〃時間を記録しておく 4 : int memo; 5 : public: Resident() : memo( の 0 / / 0 ならば泥棒を見てないことにする 6 : / / デストラクタは特に必要ないけれど・・・ virtual Resident() ; 7 : virtual int ask() = 0 : / / 聞き込みに対するリアクション 8 : 住民クラス う (List 1 ) 。 112 C MAGAZINE 1992 1

6. 月刊 C MAGAZINE 1992年1月号

特集いと G 可視文字クラス ( ? ) Pchar (PrintabIeC HARacter) の定義部分を List 13 に示す。 コンストラクタは , 変数 ch を 0 て、初期化す るという , それだけのものてある。なくて も別に害はないと思うが , 変数に何が入っ ているのかわからない状態はあまり気分が よくない もちろん , 気分だけて、はなく , 「取り敢え す初期化する」くせをつけておくのは悪い とて、はなかろう。「初期化忘れによるバグ」 は結構頻発するものて、ある。 14 行目は , 「可視コードて、あるか否か」を 返す関数て、ある。これについてはさほど問 題ないと思う。その「前」のあたりに , 少し 解説を加えたい。 Pchar. h --Printable CHARacter class ー printable character class 2 : 3 : #ifndef _Pchar_H_ 4 : #define —Pchar—H— 5 : 6 : class File; 7 : 8 : 9 : class Pchar ( 10 : protected: 11 : char ch; 12 : public: Pchar() : 13 : 14 : / / methods friend FiIe& 0EErator> 〉 (FiIe&, Pchar&) ; friend File& operator くく (File&, Pchar&) ; int is—printable() : 17 : 18 : } ; 20 : 21 : #endif 22 : 〃 end of pchar. h / / コンストラクタ / / 入力 / / 出力 演算子の多重定義 通常 , 次のように使われる (file は File 型のオ ろうか。この記述は , 「 FiIe への参照と Pch プジェクト ) 。 ar への参照を引数とし , FiIe への参照を返 List 13 中 , 15 行目 , 16 行目の , file. put(' A' ) ; す , 叩 erator> > という名前の 2 引数関数」 friend FiIe& operator > > (FiIe&, そして , 上記の関数は , 自分自身への参 のプロトタイプ宣言なのて、ある。 Pch 照を返す。早い話が , この文を実行した結 見かけは奇妙だが , これも関数なのて、あ friend FiIe& operator くく (FiIe&, 果もまた浦 le 〃なのてある。すなわち , 以 るから , 次のようにして呼び出すことがて、 Pch 下のような記述が可能になる。 きる ( file は File 型のオプジェクト , pc は Pc は , 入出力のためのメソッドて、ある。 har 型のオプジェクトて、ある ) 。 これ は , C を見慣れた目には最初かなり異様なも おわかりだろうか ? カッコの中の、、 file. p operator > > (file, (c) ・ のに映るのだが , 紛れもない関数なのて、あ ut ( ' A ' ) 〃の結果も file なのだから , その実 やむを得ずこういう使い方をすることも 行後 , 上の文は , ないてはないが , これて、はわざわざ奇妙な 最初の、、 friend" というのは , const や st file. put ( ' B' ) ; 外観を持っている意味がない。通常は , atic などと同じ「修飾子」て、ある。これは , と同じことになるのてある。出力には , $A file > > pc ; の宣言の関数のプロトタイプてある , とい こういう呼び出し方をする。 B" という文字列が現れる。 う意味て、は , 取り敢えず関係ないのて , 後 、、 FiIe & クが返り値の型て、あることはご理 、、 > > 〃というのは , 本来 , ビットシフト ほど解説することとし , しばらくは無視する。 解いただけたかと思う。そして , 次にやっ のために用いられる演算子て、あり , C におけ てくるのは「関数名」てあるはずだ。もう一 次の FiIe & が , 関数の返り値の型て、ある。 るその動作は当然固定されたものて、ある。 これは , FiIe クラスのメンバ関数の返り値と 度 ,List 13 の 15 行目をご覧いただきたい ( ア その両辺に許される型も決まっている。構 してもたびたび現れていたが , 先ほどまて、 タマの、、 friend" は , 取り敢えず無視してほ 造体など書こうものなら , たちまちコンパ の解説ては意図的に触れなかったものてあ しい ) 。 イラにはねられてしまう。 る。この関数の返り値の型ヾ FiIe & クは , 「 F C 十十て、は , そのような「演算子」 ( シフト FiIe& operator> > (FiIe&, Pchar&) ・ ile への参照を返す」という意味なのだが , 「参 operator>>" というのが , この 演算子に限らず , ほかの多くの演算子につ いても ) に , 別の「関数」を割り当ててしまう 照を返す」とはいったいどういうことてあろ 関数の「関数名」なのて、ある。随分とおかし ことが可能になっている。「演算子の多重定 うか ? な名前てあるが , そう思って眺めると , 後 義 (operator overloading) 」と呼ばれる機構 FiIe のメンバ関数 FiIe & put(char) は , ろのカッコの中が「引数」に見えてこないだ 特集 C 十十と DJGPP 61

7. 月刊 C MAGAZINE 1992年1月号

かわってくる。 まず , 前者を考えてみる。 char 型の変数 を出力すると可視コードへの変換を行うよ うな , 特殊な「ファイル」を作るのてある。 19 : } 23 : { 28 : } 20 : } 7 : PFile& PFile: :put(char c) 4 : #include く ctYEE. h 〉 3 : #include ” PFiIe. h ” Printable File class 1 : / / PFile. cc ー PFile. cc --PrintabIe FiIe class これを、、 PFiIe (PrintabIe FILE) ′′と名づ ける。このとき , 前述の File クラスがある と , その作成が容易になる。 PFile もファイルの一種て、あるから , オー 5 : 6 : 9 : 10 : 11 : 12 : 13 : 14 : 15 : 17 : 18 : 20 : 24 : 25 : 26 : 27 : 29 : 30 : 2 : 4 : 5 : 8 : 9 : 10 : 11 : 12 : 13 : 14 : 15 : 16 : 19 : if (isprint(c) Ⅱ c File::put(c); & & c > 0X00 ) { else if (c く ' form( ” %x1bC36mX02x*x1bCm ” , (unsigned else { form("}x1bC36mAXc*x1bCm ” , c + ' 0 ' ) ; return *this; char)c) ; 22 : PFile& PFile: :put(const char * while (*s) { *this; re tur n 31 : / / end of PFile . CC PTYPE の実装例 ( その 1 ) 1 : / / prYPE の実装例 ( その 1 ) main( int argc, char* argv[]) 3 : #include ” PFiIe. h ” if (argc く 2 ) { Fi le std_error(stderr) ; output. put(c) ; while (input. get(c)) { char C,• PFiIe output(stdout); FiIe input(argv[l], io_readonly, a_useonly) : return ー 1 : std—error. form("USAGE : Xs [fi lename]*n ” , argvC0]); プンしたり , クローズしたり , 入力したり ( この場合は必要ないが ) , 出力したりて、き なくてはならない。その仕様を FiIe クラスと 比較すると , 「出力の際に ( 必要ならば ) 可視 化のための変換を行う」ところが違うだけて、 ある。こんなに似ているのだ。有効に利用 しない手はない このように , 既存のクラスの仕様が要求 と似ているが , びたり一致しない場合には , OOP て、はクラスを「継承」することによって 仕様を満足させるのが基本て、ある。「継承」 は , 単なる「改造」とは異なり , 既存のクラ スにはいっさい手を加えない。継承とは , 既存のクラスをベースとして , 部分的に異 なった仕様を持つ「新しいクラス」を作成す ることて、ある。 List 10 に , PFiIe クラスの定義部分 ( PFi le. h) を示す。 出力用の関数がふたっ書かれているだけ PTYPE の実装例 ( その 2 ) st 12 を , それぞれご覧いただきたい TYPE( その 1) の main( ) 関数については Li 新しい put の中身については List 11 を , P 加したりすることもて、きる。 にない変数を定義したり , メンバ関数を追 定義」だけが行われているが , べースクラス まま利用可能て、ある。この例て、は関数の「再 なども含め , すべて FiIe クラスのものをその する。残りのメソッドは , コンストラクタ て、ある。これらの関数のみを新たに再定義 も , はるかにやりやすい た , 後々 2 バイト文字への対応を考えた場合 前者よりもこちらのほうが好みて、ある。ま の」のほうを主体に考える。筆者としては , 加えたわけて、あるが , 今度は「出力されるも を考えてみる。先ほどは , 「出力先」に手を るクラスを作成する ・出力時に可視化のための情報も出力され 次に , ふたつめの 60 C MAGAZINE 1 2 1

8. 月刊 C MAGAZINE 1992年1月号

実力養成講座 9 LiSt 1 : 3 : 5 : 6 : 9 : 10 : 11 : 12 : 13 : 14 : 15 : 白倉伸一郎・山本浩文 整数型の配列の派生 IntArrayedcIass:publ ic ー Arrayedclass class 2 : private: int data[100] : 一 / / 配列の定義 4 : public: IntArraycIass(){ / * 配列の初期化手続き * / } 〃コンストラクタ スタートアップ C 十十 ています。 ウインドウブログラミングなど , 多くの 似通ったクラスを定義しなければならない 場合にも , この手法は応用て、きます。抽象 クラスとしての基本クラスて、は , ウインド ウが持つ機能の枠組みを定義し , 目的に応 じたウインドウクラスを派生させていくと VOid i nt next(){ static int = 0 ; if( datacount 〉 1 ) return a[i 十 + ]; nextput(int* value)( if( datacount = 100 ) return ー 1 ; a[datacount + 十 ] = *value; return 1 : そのライプラリを派生して利用するのは , どのような条件が必要なのかなど階層の枠 組みを示すものが , その抽象クラスなのて、す。 , こて、注意していただきたいのは , その クラス階層が , ストリーム形式のメカニズ 「′お企月はゲームでかばちょ この連載も , はや 9 回を数えましたが , ど うも今まて、おカタい話が多かったような気 がします。そろそろ ( 最初から ? ) 肩がこっ てきたのてはないかと思います。 実は , 執筆陣も同じ気持ちてす : ー ) 。と いうわけて , こらてちょいと息抜きとい きましよう。幸いにして , 本稿のタイトル は「実践編」てあって , 「実用編」てはありま せん。実用性・目的などまったく考えず , 簡単なゲームを作ってみました。 ぶっちやけた話 , いつもなら題材を決め るのに 「今度はどういう機能の話をしようか ? 」 「あれはどうかな。たしか理論編て、も解説さ ムを持つ配列て、あるというのは , 基本クラ スが ArrayedClass という名前を持つからて、 はなく , 読み込み (next) と書き出し (nextp (t) というふたつの操作がて、きるからなのて、 す。そうしたことをこの抽象クラスは示し 実践編 れてたし」 「もしあれを使うなら , こういう応用例なん かどうかな」 「誌面にリストおさまるかなあ」 などと , どちらかというと言語機能を中心 に考えているのて、すが ( 締め切りが過ぎても 決まってないことが多かったりして・・ あっ殴らないて、つ > 編集の方 ) , 今回はそう いうのまったくなし。とにかく何かゲーム を作ってみよう , という動機が先にあって , 作ってみたらやつばり C 十十は楽だったな あ , という話てす。 第どんなゲームにしようかなっと あまり大げさなゲームもどうかな , とい うことて , 簡単な追っかけっこゲームにし いうわけて、す。 関係するいろいろな事項について解説する 次回は , その点もふまえてクラス階層に っていたりするわけて、す。 える部分が , 実は非常に重要な役割りを持 った部分があって , 高級な C 言語のように見 さに近づいてきました。 C 言語とはかなり違 やっと , C 十十の持つ本当のパワーや面白 予定て、す。 っきの住民や , 警察嫌いて、情報を教え い素直に教えてくれるが , なかには嘘 りだ。泥棒を目撃した住民は , たいて ない。住民への聞き込みだけが手がか いるのか , 警官は直接知ることはてき 官が泥棒を追っかける。泥棒がどこに 筆者「町があって , 住民がたくさんいる。警 れをベースに , 少し趣向を加えてみます。 の「鬼ごっこ』に相当するものて、すが , 「泥棒と警官』という遊びがあります。日本 きて、はないて、しようか ( いい訳 , いい訳 ) 。 いる今こそ , CUI をもう一度見直してみるべ ースて、済む。 GUI が手放して、もてはやされて なる ) , ユーザインタフェイスもテキストべ 思考ルーチンもいらないし ( 乱数て、なんとか てみました。これならばコンピュータ側の スタートアップ C 十十 111

9. 月刊 C MAGAZINE 1992年1月号

long tell(); . 101 : / / buffer control File& raw(); ・ 109 : / / error handlers List 9 FiIe& setbuf(int size, char* buf) ; operator void error(); 57 : / / error handl ing int is_open() ; int writable(); int readable(); / / other status queries int good(); 48 : 49 : 50 : 52 : 53 : 54 : 55 : 56 : 58 : 59 : 60 : 61 : 62 : 63 : 64 : 65 : 68 : 69 : 70 : 72 : 74 : 75 : 76 : 77 : 78 : 81 : 82 : 83 : 84 : 86 : 88 : 89 : 91 : 92 : 93 : 94 : 95 : 96 : 98 : void c lear(state_value f ニ—good) ; / / poorly named void*() ; void set(state_value f) ; / / set corresponding but VOid unset(state_value f) : FiIe& failif(int cond); void check state() ; 66 : / / character IO Fi le& get(char& c) : File& put(char c) ; File& unget(char c); Fi le& putback(char c) 73 : / / char* IO FiIe& put(const char* FiIe& get (char* s, int n, FiIe& getline(char* s, int FiIe& gets (char **S, char 80 : / / binary IO FiIe& read(void* x, int sz, / / clear corresponding bit a synonym for unget Char terminator n, Char terminator terminator File& write(void* x, int sz, int n); int n); 85 : / / formatted IO Fi le& form(const char* File& scan(const char* 90 : / / buffer IO FiIe& flush(); FiIe& flush(char (h); / / call stdio flsbuf int f i 1 1 ( ) ; / / ca 11 / / position control FiIe& seek(long pos, stdio filbuf int seek_mode= の ; / / default seek mode=absolute 99 : ー 100 : ー 102 : 103 : 104 : ー 105 : ・ 106 : } ; 107 : ー 108 : ー 110 : 111 : : 112 : 114 : 115 : FiIe& setbuf(int buffer—kind) ; / / legal vals: IONBF, IOFBF, I OLBF extern void verbose_Fi le—error_handler(const char*) : extern void quiet—Fi le—error_handler(const char*) : 113 : extern void fatal-File-error handler(const char*) : extern one_arg_error handler_t Fi le error handler : extern one—arg—error handler—t set Fi le error handler(one arg— error hand 1 er 特集いと G より C 十十らしいプログラミング手法につい 機能変更など , 前半て、は語られなかった , 後半て、は , 演算子の利用や , 継承による 赦願いたい はないのて、 , この程度に留めることをご容 可能て、あるし , 本稿は libg + + の紹介記事て、 う ) , 本稿て、そのすべてについて語るのは不 て、ある「クラスライプラリ」の違いて、もあろ だが ( そこが単体としての「クラス」と集合体 F ⅱ e クラス て述べたい iO readonly, 特集 C 十十と D 」 GPP 55 a useonly) ・ file. open ( "filename きを行う前に 言しただけの状態と似ている。読み書 FILE* fp , づけはなされていない。 C て、 , ( 当然だが ) ディスク上のファイルとの関連 このようにして生成したインスタンスは , 上記のようなときに , これが使われる。 File file 作るだけものて、ある。 ラクタは , インスタンス ( オプジェクト ) を 最初に書かれている引数なしのコンスト ただきたい List 9 の 17 行目から 22 行目にかけてをご覧い FiIe には 6 種類のコンストラクタがある。 のファイルを include する必要がある。 ものて、ある。 FiIe クラスを使用するとき , め , 先頭にアンダースコアいつをつけた いう事情から , file. h との衝突を避けるた ル名て、は , 大文字小文字が区別されない」と るが , DOS へ移植するる際に「 DOS のファイ 来は、、 FiIe. h 〃という普通のファイル名て、あ E. H" というファイルに記述されている。本 を List 9 に示す。これは , djgpp て、は、、 FIL 化するためのクラスて、ある。その定義部分 が示すとおり , 「ファイル」をオプジェクト libg 十十に含まれる File クラスは , その名

10. 月刊 C MAGAZINE 1992年1月号

特集いと G 。たとえば , ファイル dusty. h が , 最近 C 十十て、手 まず , printf のように , C て、は , 使用前の宣言が任 い : 直しされた shiny. h をインクルードしているかもしれ 意て、あるような関数は , C 十十て、は , オプション一 W a Ⅱの有無にかかわらず , 警告が出され呼び出しが失・ない。この場合 , shiny. h が extern " C 十十 " て、囲まれ : ない限り , スコープルールは狂ってしまうだろう。 敗してしまう。第二に , C て、の宣言 int atoi() は ato i は int を返すという宣言てあるが , GNUC 十十て、は , : 構造体タグは , そのままて、は識別子名前空間には持 : っていかれないだろう。関数は多重定義て、きない 関数 atoi は引数を持たず , int を返すという意味にな まして自動的には多重定義て、きない。そしてコンパ る。したがって , 次の関数呼び出し , : イラは C 十十とは違う構造を扱うだろう。 int ー =atoi("20") ; libg 十十クラスがサポートするファイルについて は , GNU C 十十て、は , 工ラーになる ( 利用者がフラ : は , GNU C 十十ライプラリについてくるドキュメン グ -fno-strict-prototype を指定しない限り ) 。 GNU トて、述べられている。 C 十十の配布て、ついてくるヘッダファイルは , よく使 : われるほとんどの関数について適当な宣言を提供す 6. による C 十十言語への拡張 その場合 , GNU C 十十のヘッダファイルは , 標準 : C ヘッダファイルと同じ名前を持つ ( stdio. h のように ) 。 ・ 6.19 operator new の制御 そしてそれらは C バージョンに優先されるべきて、あ 現在 , 利用者は演算子 new をたいへん多く使ってい る。したがって , GNU C 十十のヘッダファイルが常 に /usr/include のような標準ディレクトリの前のサー る。普通 , 演算子 new は size 引数を持って関数 bui : ltin new を呼び出し , builtin new は少なくとも si チされるように配置されなければならない Rel. 1.35.0 て、は , extern "C" と extern ' ℃十十 " と : ze バイト長の記憶領域へのポインタを返す。 size 引数 ・に加えて , 演算子 new へ複数の引数を渡すことは可能 いう構造がサポートされた。これを使えば , C ヘッダ 三てある。 ファイルをインクルードして , それを間題なく使い そして , それらは , 利用者が望んだ何かを返す関 たいという場合には , 次のように行うことがて、きる。 数 user new に渡される。 user new を定義するこ extern ” C' とは , 利用者の責任てある。関数 user new は , ほ : かの関数がそうて、あるように多重定義されてもよい #include <dusty. h > それは暗黙のうちに多重定義されない。例をあげる (List 6 ) 。 しかしながら , 問題が常に単純て、あるとは限らな この例て、は , user new は , 内蔵の演算子 new に ヨ対し , realloc スタイルの振る舞いを与えるように定 : 義されている。 new のほかのすべてのセマンティクス : は保たれる。初期化は , 厳密に同じ方式て、行われる。 しかしながら , そういう関数を使う場合 , 気をつけ なければならない。それは user new が返したアド レスが , 正しいアドレスて、ない場合 , どんな行動を : 取るかはコンストラクタの責任て、ある。 : 6.21 switch 文の範囲指定 GNUC 十十の switch 文の拡張は , case の値に範囲 : 指定を許す。たとえば , 関数の引数の「文字種」を出 lSt / / C + + で realloc と等価な new を作成する inl ine VOid * user—new (int new—s VOid *Old—ptr) 2 : return (void*) real 10C (old—ptr' new—s ize) : 4 : 5 : 6 : 7 : char *manipulate—string (char *string) 8 : int len = strlen(string) + 1 ; 9 : = strcpy(new char[len], string); Char *S 10 : 11 : char *t = new (s) char Clen * 2 ] ; 12 : 13 : return strcat (), string) ; 14 : 特集 C 十十と DJGPP 51