るのてほ , C 十十の設計としては不十分て、 組み合わせによって構成されているわけて、 このように , クラスの中にほかのクラス す。実際に構造体を機能的な側面から詳細 す。 を含むような構造を持ったものを入れ子に に分析すると , 今回述べたように , ひとつ こうした複雑な形態の構造を持ったもの なったクラス , 複合クラスなどと呼びます。 のクラス ( 構造体 ) に見えるようなものて、も , を簡潔に表現するために C 十十クラスは用い この方法て、のデータ自身は , スタックから いくっかのプリミテイプな機能の複合体て、 られるといっても過言て、はありません。プ 独立して操作されることになります。つま ある場合が非常に多いはずて、す。たとえば , ログラムの対象をより簡潔化するためにク り , スタックのデータに着目したクラスと ラスを機能させるわけて、す。 スタックのような若干複雑なデータ構造て、 スタックの操作に着目したクラスのふたっ あっても , いくっかの単純なデータ操作の あまりこうした側面について解説される によって , スタックを表すクラスを構成す るわけて、す ( Fig. 7 ) 。したがって , この配列 Fig. 6 文字列と整数テータの構造体配列 て、実現されているスタックのデータ管理を リスト構造に変更したとしても , プログラ クラススタック ム上の変更は少なくなります。 まめ 文字列 整数データ 文字列 整数データ 文字列 整数データ 一般に , C 十十て、用いられるクラスを設計 する場合 , C 言語て、設計された構造体を中心 にその構造体に対して ( 正確には , 構造体の メンバに対して ) 行われる操作を合わせ , ク ラスを考えることが非常に多いと思います。 しかし , C 十十て、用いられるクラスを C 言語 の構造体を単純に拡張したものとして捉え Fig. 7 スタックのテータと操作 push 作 構造体 POP タ一 ス ス ク タ ク 文字列 整数テータ スタ クテ スタ クデ スタ クテ 代入 参照 push 操 作 PO P class sdata { private: char* string; i nt data; public: int dainyuu(sdata&); sdata sanshou (void); class stack { private: sdata ad—arytSlZE]; int public: int push (sdata&); sdata pop (void); 136 C MAGAZINE 1991 11
Li st 継承の利用 (a)o void d0Sink(Sink* sink9 char* bufY sink くく buf; (b) 、 BeautifulSink mysink(8) : つ : do S i nk (&mys ink, æ"ThiS i s → .Longer 新 a れ 1 ine width•- 仮想関数 ところて、 , C 十十て、はく仮想関数 > と呼ば れる形式のメンバ関数が用意されています。 仮想関数は , 宣言時にⅵ rtu 引というキーワー ドをくつつけて通常のメンバ関数と区別し ます。 仮想関数は , 通常のメンバ関数とまった く同じように書けますし , また , 使うこと がて、きます。ただひとつ違っているのは , そのメンバ関数をクラスへのポインタを介 して呼び出したときの振る舞いて、す。 仮想関数を含むクラスは , 内部に ( 見えな い ) 関数ポインタのテープルを持ちます。 のテープル ( 仮想関数テープルと呼ばれる ) には , ⅵ rtual と宣言されたメンバ関数のア ドレスが収められています。クラスへのポ インタを介して仮想関数が呼び出された場 合 , 通常のメンバ関数呼び出しとは異なり 直接その関数が call されるわけて、はありませ ん。仮想関数テープルを参照して間接的に 呼び出される仕組みになっています。 なんて、そんな回りくどいことをするのか というと , まさに , 先ほど持ちあがったよ うな問題を解決するためなのて、す。 List クラスのインスタンス生成の切り換え 皿 ( 血む argc.ghar** argv) ノ / コマンドライン引数の解析など 。一 / / 基本クラスへのポインタ Sink* ink ; switch ( 引数に応じて ) { case イ美しい流し」の要請 : sink 咢れ BeautifulSink()i 数 ) : - 。 / / BeautifulSink クラスのインスタンスを生成 , 、 - break ; case •r 清潔な流し」の要請 : 第 sink れ e 豊 SanitarySink(F 数 ): / / SanitarySink クラスのインスタンスを生成 break; default: sink = れ e Si 心 ( 桁数 ) : //Sink クラスのインスタンスを生成 。 = / / ごこから先は、 *Sink がどのクラスに属するのか 〃区別する必要はない char bufCBUFSIZ] : while (gets(buf))J *sink くく buf; st 7 のように , どのクラスのインスタンスを コラム protected メンバ 生成するかを切り換えるだけて、よいのて、す。 なんと , 継承を利用すればいいことづくめ Padding( ) は Sink クラスのプライベー すなわち , 先ほどのクラス Sink の定義を次 て、はありませんか。 のように書き換えます。 トメンバにアクセスしなければなりませんが , と , まるて、万能ネギ的アイテムのように class Sink { プライベートメンバは , そのクラスのメンバ 述べましたが , 肝心なことを確認しておか p 「 otected : 〃派生クラスからアクセス 関数 ( あるいはフレンド関数 / フレンドクラス ) なくてはいけません。 doSink の例て、いく 〃できるようにする 以外からアクセスできないのが身上です。た と , doSink 関数は sink- > operator < < ( ) とえ派生クラスでもアクセスは許されません。 を呼び出し , さらにその中て、 sink->paddi このようにすれば , 必要なメンバが派生ク 親子の間柄なんだから , それくらい融通をき ng ( ) が呼び出されます。ここて、 , はたして ラスからアクセスできるようになり , スムー かせてくれたっていいじゃないか , といって : padding ( ) 期待どおりに BeautifulSink : も通りません。そのくせ友達には覗かせると ズに Sink を継承することができます。 が呼び出されてくれるのて、しようか ? 答 何でもかんでも派生クラスに公開すればよ いうのだから , なんだか「大人の話だ , 子供は えは否て、す。 外で遊んでなさい」という調子で悔しいかきり いというものでもありません。子供にスネを doSink( ) に引数が渡された時点て、 , Sin かじられすきては大変ですし , 子供は子供で です。 k へのポインタに型変換されていますから , 傍若無人に育ってしまいます。親としての体 その融通をきかせるのが , キーワード p 「 0 : padding ( ) 呼び出されるのは当然 Sink : 面を保ちつつ , 子供をりつばに育てあける。 tected です。 P 「 otected 部は , 派生クラ にほかなりません。あれもこれも , 全部ぬか そうありたいものです。 スにだけアクセスを許した p 「ⅳ ate 部である 喜びだったわけてす。せつかくの苦労も水 と考えることができます。 の泡てすね。 140 C MAGAZINE 1991 11
スタートアップ C 十十 実力養成講座 7 Fig. 3 モジュール構成 問題解決モジュール スタックポインタ スタック スタック実現モジュール , 。 p ush POP 期をとるための操作が必要になってしまい たがって , 今回の問題においては「要素と 列て、はなくリスト構造に変更するような場 なる文字列と整数のペアをデータとし , 後 ます。そのうえ , 文字列と整数データを一 合は , 多くの変更を要します。したがって , 入れ先出しによる , 追加と取り出し操作が 組として扱うという問題の意図に反します。 拡張性の悪いプログラムとなってしまいま そこて、 , 次の方法として Fig. 6 に示すように なされるスタックを実現させる。そのスタ す。 文字列と整数データを構造体にし , その配 ックのデータ構造はクラスによって実現す 十の機能の利用 列をクラス内のデータとする方法が考えら る』ということになります (Fig. 4 ) 。しか し , 先ほど述べたことと同様に『クラスに れます。しかし , この文字列と整数データ のまとまりには , 値の代入と参照を行う操 よってデータを複数個管理するために , そ て、は , C 十十の持っている機能を用いて , 作が必要てす。したがって , 文字列と整数 の領域をクラス内部て確保するための方法』 このスタックを実現する場合どのようにな データのまとまりをもクラスにすることに が問題となります。 るか考察します。先に述べたいくっかの問 より値の代入と参照を行う操作が実現てき ひとつの方法として Fig. 5 に示すような文 題点に注意しながら複数のアプローチを試 字列と整数データをそれぞれ配列とする方 ます。この方法て、は , スタックを実現する みます。 クラスの中にペアとなったデータを表すク 法があります。ところが , この方法て、は , C 十十のクラスとは , データとそれに対す ラスが含まれるということになります。 文字列と整数データが独立してしまい , 同 る操作 ( 処理 ) の融合体と定義されます。し Fig. 5 文字列と整数データの配列 Fig. 4 スタッククラス } クラススタック スタッククラス 文字列 整数テータ 文字列 文字列 整数データ 整数テータ 文字列 整数テータ push 作 push 作 POP POP スタートアップ c 十十 135
実力養成講座 7 コラ、ム 動的結合 仮想関数の呼び出し機構を指して , 「動的結 合 (dynamic binding) 」や「後期結合 (lat e binding ) 」と呼ぶ場合があります。 C 十十 の入門書などを読んでいると , 時おりそうい う表現を見かけます。 しかし , オプジェクト指向ギンギンな人を 除いて , これは非常に誤解を招きやすい表現 なのて , とくに C 言語から C 十十に移行しよ うとしている人は , 読みとばしたほうがよい でしよう。というのは , ダイナミック・リン キングやオーバレイマネージメントがからん protected class Sink { virtual void padding( ) ・ を見失わずにいられる」ということて、す。 っそうとも , 常に本来のアイデンティティ 基本クラスへのポインタや参照型に身をや 「何やらそういう機構を利用すれば , たとえ ものは忘れてしまっても構いません。要は ルにお出ましを願いましたが , 別にそんな , こて、は説明の都合上 , 仮想関数テープ が呼び出されるのて、す。 晴れて正しく BeautifulSink : : padding ( ) tifulSink クラスのテープルて、す。ゆえに は , あくまて、も ( 本来 *sink が属する )Beau 呼び出し時に参照される仮想関数テープル トされてしまっても ,sink->padding() の タが基本クラス Sink へのポインタにキャス 数の例のように , BeautifuISink へのポイン 間接的に呼び出されます。たとえ doSink 関 が呼ばれる場合には , テープルを参照して ルを持ちます。ポインタ経由て、 padding( ) とその派生クラスは , すべて仮想関数テープ 関数にしたとします。すると , クラス Sink のようにして , Sink : : padding ( ) を仮想 となると , 初めからすべてのメンバ関数 クラスの設計 できかねない C 十十においては , 必ずしも仮 想関数だけが「動的に結合」されているとは限 らない , という考え方もできてしまうからで す。 また本稿では , 仮想関数の呼び出し原理を 単純化して解説していますが , 実際には効率 などのため , 結構複雑なものになっています。 インプリメントは例によって処理系ごとに微 妙に異なりますが , 基本は「 The Annota ted C 十十 Refe 「 ence ManualJ で詳 説されています。 ( コンストラクタを除く。コンストラクタは 仮想関数にはて、きない ) を仮想関数にしてお くのが無難だといえます。仮想関数は , 従 来形式のメンバ関数に対してほば上位互換 なのて、すから。 ただし , 仮想関数はメリットも大きいぶ ん , 呼び出しにコストがかかります。 C 十十 は C の子供てあり , 効率第一主義を奉じる言 語て、す。コストが高くつくものは , なるべ く必要最小限におさえたいという要望もあ って当然て、す。そうすると , プログラマは あるメンバ関数を仮想関数にするかどうか , クラスの設計時にいちいち頭を悩ませるこ とになります。 仮想関数の継承 ( またはオーバライド ) の され方て、すが , 基本クラスて、いったんⅵ rtu al と宣言されれば , 派生クラスて、はそのメン バ関数は , 黙っていても仮想関数になりま す。 逆に , 基本クラスて、仮想関数て、はないメ ンバ関数を派生クラスて、いきなり仮想関数 にすることはて、きません。仮想関数テープ ルは , いわばディスパッチテープルになっ ているわけて、す。 つまり , 仮想関数にするかどうかは , 基 本クラスの段階て、すて、に選択を迫られてし まうわけてす。基本クラスを設計するとき にある程度先を見越しておかなくてはなら ないということて、す。 最初から完璧な設計がて、きれば理想て、す が , なかなかそうもいきません。やはりコ スタートアップ C 十十 ストは度外視して , 初めからすべてのメン バ関数をⅵ rtual にしておくべきなのて、しょ うか。それとも , 派生クラスて、必要になった 時点て , フィードバックさせて後知恵的に基 本クラスをいじくるべきなのて、しようか。 経験上 , おばろげながら指針めいたもの が見えつつあるのて、 , 次回あたりにもう一 度触れたいと思います。 を三ティスクの ef d さて , こまて、述べた方針て、 , efold アプ リケーションを完成まてこぎつけさせるこ とがて、きると思います。付録ディスク (YCM AGA \ CPP ) に , やっていることは同じて、す がちょっとはマシな ( ? ) efold を収録してお きますのて、 , 参考にしてください 再三述べたように パディング処理は個 人の美的感覚に依存します。読者の方々の 好みに合う padding ( ) を実装させてみるの もおもしろいかと思います。たとえば , 空 白をいじくるための着目点を中央や両端に 求めるのて、はなく , ピリオド / コンマ / セミ コロンなどの句読点・記号の直後の空白に 着目させてみれば , もう少し万人向けの efo ld がて、きあがりそうて、す。 ところて、 , そもそも Sink: :padding() が派生クラスて、かならずオーバライドされ るものだとすれば , Sink 自身が padding( ) という考えも を実装している必要はない , 出てきます。 それに , これはソースの美観の問題て、す が , 基本クラス Sink と派生クラス群とが同 格に入り交じるのも , なんだか子供の喧嘩 に親がしやしやり出るような , そんなアン バランスな印象があります。基本クラスた るもの , もっとデンと構えていてもらいた いものて、す。 付録ディスクの efold て、は , そこいらのもや もやを純粋仮想関数という大仰な名前の仕 ラムを作成しながら解説することにしまし これについては , また次回 , 別のプログ 組みを使って解決しています。 よう。 スタートアップ C 十十 141
を送ることによって起動されます。かんじ んなことは , あるクラスのオプジェクトを 作ったときには , そのオプジェクトはその クラスのメソッドと属性をすべて自動的に 継承する , という点て、す。プログラマは , 複数の親クラスから子クラスを合成するこ とがて、き , 子クラスは複数の親のメソッド と属性を継承します〔訳注 : メソッド (met hod) = オプジェクトをデータ部とアクショ ン部に分けたときの , アクション部を構成 する個々の手続き。 C 十十のクラスて、は、、メ ンバ関数〃がメソッドに相当する〕。 いちばん普通の使い方は , ツールが提供 しているクラスを利用し , また , 以下のよ うなオプジェクト指向のテクニックを使っ て , 子クラスを自作します。 ・親クラスのメソッドをすべて継承し , そ れにさらに独自のものを加える。 ・親クラスの一部のメソッドを継承し , 他 は自作のメソッドて、オーバライド (overr ide) 〔 C 十十語て、は、、オーバロード (over load ) 〃〕する〔訳注 : オーバライド , オ ーバロード = 旧定義に代わって新定義が 支配力を持っこと〕。 ・複数の親クラスの属性とメソッドを , 多 重継承により結合する。 その結果プログラマは , ツールが提供し ている視覚物クラスのライプラリを利用し て , 自分好みのオプジェクトを作ることが て、きます。そうやってオプジェクトをフル に活用しても , クラスライプラリのコード は 1 行も覗く必要はないのて、 , ツールのべン ダ側は楽て、す。コードはすべて , オプジェ クトの中へ封じ込まれています。これはま さに , 両方が得をする , という場合の典型 的な例て、す〔訳注 : べンダはソースコード を提供しなくてもよい。ユーザ ( プログラマ ) はカスタマイズのためにソースコードをい じくる必要がない ( 既存クラスから子クラス を派生させるだけて、よい ) 〕。 【利点】ツールが提供している , テノヾック・ 済みの大量のコードライプラリを利用て、き , プログラマはそれにわずかなコードを加え るだけて、 , 相当量の仕事がて、きてしまいま す。ツールには , オプジェクト指向のデバ ッグ環境が提供されている場合が多く , そ れを使ってメッセージ上にプレークポイン トを設定し , オプジェクトのステートを調 べることがて、きます。その環境は , クラス の階層をツリー図て、示すプラウザを提供し ています。オプジェクトは他のオプジェク トにメッセージを , イベントループを介し て間接的にて、はなく , 直接に送れます〔訳 注 : プラウザ (browser) = 定義済みのクラス の階層を展望しつつ , 必要なものを拾って いけるプログラミングッールて、 , OOP には 必須といわれる。なぜか , C 十十の処理系に はまだ標準装備されていない〕。 オプジェクトという枠組みのおかげて、プ ログラマは , プログラムを再利用性のある オプジェクトの集まりとして書けます。継 承と , メソッドの、、オーバライド という 機構によって , 既存のコードの再利用性が 増します。また , プログラムのメンテナン スもやりやすくなります。オプジェクトは , それぞれが自己完結しているのて、 , 既存の コードをほとんど変えずに , 新たなオプジ ェクトを追加することによって , 機能を増 やすことがて、きます〔訳注 : プログラムの 新機能を新オプジェクトの追加によって実 現する場合 , 既存のコード ( = 既存のオプジ ェクト ) には手を加える必要がない〕。 【費用】オプジェクトの環境は , C 十十や Smalltalk といったオプジェクト指向言語を 実際に必要とします。その大きな障害が , 学習曲線〔が急峻て、一朝一タて、はマスター て、きないこと〕て、す。オプジェクトべース の環境と , ツールに付随しているクラスラ イプラリを使いこなせるようになるまて、 , 最高て、 6 か月はかかるてしよう。とくに , び っくりするほど大規模なクラスライプラリ を理解するのに , 相当の時間を要します。 初心者の多くが , 名前も , 目的も , メッセ ージもそれぞれ違う , 数百ものクラスがあ るのを見ただけて、 , ビビってしまいます。 しかし , 最初はわすかなクラスからスター トして , 徐々に知識を増やしていけばよい のて、すから , そんなにたいへんて、はありま せん。 もうーっの問題は , ツールが提供してい るライヴラリから派生て、きないようなグラ フィクスのクラスは , 自分て、書かなければ ならないことて、す。そして , この種のツー ルが高価なことて、す ( 7 , 000 ドルするものも あります一一一しかし , 前に述べたように そのツールが優秀なら高くはありません ) 。 ランタイムサイズもメモリ食らいて、 , IM バ イトを超えることもあり , お金がかかりま す。オプジェクト指向言語て、作ったアプリ ケーションは , 多態的なメソッドをランタ イムに結合するため , 伝統的なツールて、作 った同等のプログラムよりも , やや非効率 になるきらいがあります〔訳注 : 多態的 (p olymorphic) = ーっの同名のメソッドが様々 に ( 複数に ) オーバロードされていること〕。 Robert Orfali 氏は , カリフォルニア州サン ホセの旧 M の顧問プログラマ。彼は , クライ アントーサーバ方式のトランザクションを 可能にするプラットホームである TxE の設計 者である。 Dan Harkey 氏も旧 M の顧問プログラマで , TxE の首席開発者である。 本稿は , Client-Server Programming wi th OS / 2 Extended Edition (Robert Orfali a nd Dan Harkey/Van Nostrand ReinhoId, 1991 ) からの抜粋であり , 発行者の許可を得 て掲載したものである。 GUI プログラミングの高速化 29
スタートアップ C + + = = ロバ ら = = ロ 設 造 実力養成講座 前回までで C 十十のおおよその文法事項は解説したつもりで す。今回から , 理論編ではプロクラムの根幹どよる設計の話を実例 をあげながら , 実践編では簡単なアプリケーションを作りながら C 十十プログラミングの実際を解説します。 理論編 第 木戸研ー / 龍崎昌平 ックを用いて複数個管理するプログラムを そのデータに対してどのような処理を必要 ′多ノクを使ったプログラム 考えます。プログラムの設計段階て、はデー とするかにも注目しなければなりません。 タ構造の設計とデータ構造の操作を含めた さらに , クラス間て、行われる演算なども重 問題を解決するアルゴリズムを考えます。 要な要素として考慮する必要があります。 C 十十によるプログラミングて、は , プログラ スタックとは , データを追加された順に 今回はスタックを用いたプログラムを例と ムに含まれるクラス設計 ( クラスデザイン ) 保存し , 保存されたデータを取り出す際に して , クラスをどのように設計 , 実現する が重要な要素てす。これは , C 言語において は , 最後に追加されたデータから順に取り かを考えます。 データ構造を決定する構造体の設計以上に 出す LIFO (Last ln First Out) 操作がなされ 重要な作業て、す。なぜなら , クラスはデー スックとは るデータ構造て、す。そのデータを取り出し タだけて、はなく , そのデータに対する処理 たり代入する位置を示すためにスタックポ も含んて、いるからて、す。ゆえに , クラスの インタを使用します ( Fig. 1 ) 。 文字列と整数を一組とするデータをスタ 設計て、はデータのみに着目するのて、はなく , Fig. 1 スタック 取り出す ( Pop ) 代入する (Push) テータ データ テータ スタックポインタ テータ構造 スタック領域 スタートアップ C 十十 133
スタートアップ C 十十 実力養成講座 7 えるために , 母屋ごと建て直してどうしま す。それじゃ何のためにせつかくクラス化 したのか , 本末転倒て、はありませんか。 List Sink : : cont 中の空白を適当に調整するメンバ関数 1 : void Sink::padding() nintrest 行端まであといくつ ; 3 : •nt r 右の方の着目点 : 4 : int 1 キ左の方の着目点 : ~ 5 : 〃行端が揃うまで while (rest-- 〉の { if ( 1 の 1 ー 0 い、第右の方の着目点 : if (rest % 2 = 9 : , 右の方の単語間の空白を増やし、着目点を左にずらす ; 10 : else 11 : 左の方の単語間の空白を増やし、着目点を右にずらす : 継承を使いましよう , 継承を。 つまり , Sink を基本クラスとして , 変更 したい部分 ( この場合はメンバ関数 padding) だけをオーバライドしていくわけて、す。 たとえば ,List 4 や List 5 のように Sink を 継承して , いくらて、も好き勝手な「流し」を 実装することがて、きます。着流しだろうが , 垂れ流しだろうが , 何て、もござれて、す。 継承を使えば , 労せずして変更したい箇 所だけを変更て、きるばかりか , 派生したク ラス群を統一的に取り扱うことがて、きる , というおまけもつきます。 お粗末な例かもしれませんが , List 6(a) のような関数を作ったとしましよう。派生 切り換える , というアプローチをとれば済 クラスへのポインタは , 基本クラスへのポ ことにあるわけて、すから , そういう横槍も みます。しかし , ここて、 padding() は Sink インタへ暗黙のうちに型変換て、きるのて、 , あだやおろそかにはて、きません。もし , 採 クラスのメンバ関数て、すから , 拡張性に難 List 6 (b) のように使えたりします。 用したルールが個人の美的感覚 ( 趣味といっ BeautifulSink へのポインタが , 基本クラ てもいいかも ) に合わなかった場合 , paddi ありの感があります。 Sink : : padding2 や : padding3 といったメンバ関数を別 ス Sink へのポインタに型変換されて doSink ng( ) の実装を個々人の好みに合わせて変更 Sink : 途設けてもよさそうなものて、すが , メンバ ( ) に渡されます。 BeautifuISink だろうが S することになります。 anitarySink だろうが , Sink の派生クラスて、 関数はクラスに囲い込まれています。何だ かといって , 世の中には「これぞ私が求め ありさえすれば何て、も受け付けてくれます。 か妙にちまちました部分をいじくらされる ていたパディングだ ! 」という人もいるかも いったん基本クラスへのポ このように ことになってしまいます。 しれません。それはもう < 神のみぞ知る > て、は , 気前よくクラスを丸ごとどーんと インタ ( または参照型 ) に型変換して取り扱 て、す。結局 , 複数のパディングルーチンを うようにすれば , 本来の基本クラスとまっ 差し換えてしまってはどうて、しよう。たと 実装し , 切り換えて使えるようにしておく たく同じように取り扱うことがてきます。 ということに落ち着 えば Sink2, Sink3 といった別のクラスを用 ことはない , インスタンスの宣言の部分 ( 上の例ていう m きます。と言葉て、いうのは簡単てすが , 実 意するわけて、す。これは , padding の実装だ ysink の宣言 ) を取り換えるだけて、プログラ 際にはどうすればいいのて、しようか。 けが違って , ほかは Sink と同じになってい ムの残りの部分はまるて、いじくる必要があ もし , 従来の C 言語のように , パディング ないと混乱の種て、すから , Sink の定義をコ 処理がべタな ( グローバルな ) 関数として実 ピーしてきて , 間違いのないよう留意しな りません。 あるいは , 先ほど持ち上がった「切り換え」 現されているのならば , 話は早かったわけ がらクラス名を置換して・・ ちょっと待ってください。軒先を取り換 てす。いくっか複数の処理関数を用意して の問題も一気に解決て、きます。つまり , Li List Fig. 2 efo で整形したサンプルテキスト lt9s easy tO decide on just what kind Of house you want, hOW many bedrooms you and whether it should have a need, fireplace or a big backyard. The real challenge ー is where you are going tO get especially if you're a your mortgagey first time home buyer. ( 桁数 4 の List Sink の継承 ( 清潔な流し ) class SanitarySink : 0 Ⅱ bl ic Sink { void padding(); publ ic: SanitarySink(int Ⅱ ) : Sink(n) { ) / Sink の継承 ( 美しい流し ) class BeautifuISink : public Sink ( void padding(); publ ic: Beautifu1Sink(int n) : Sink(n) ( ) / / 清な流し ~ ー〃オーバーライド 〃美しい流し 〃オーバーライド スタートアップ C 十十 139
スがまぎれこんて、います。それは , いった ろうように調整してやればよいわけて、す。 しようか (List 3 ) 。 い何て、しようか。 これて , 行端は見事そろうはずて、す。さ 基本的にはこれだけ。 実際に実装するにあたっては , タブ文字 っそく , このルールて、先ほどの英文を処理 - そろえ処理のインプリメント の取り扱いや , 段落の区切りをどう見るか してみるとどうなるか見ましよう (Fig. 2 ) 。 なんだか , ちょっと間抜けて、す。 など , いくつか細かい問題も出てきます。 まあ , これは本題て、はないのて、適当に決め とりあえず , Sink: :padding() を実装 「もっとうまく空白を入れられるんじゃない 打ちしましよう。 してみましよう。まず何事も戦略からて、す。 か」 空行か , 空白 ( タブもしくはスペース ) て これは , 行端をそろえるために出力バッ そんな感じがしますね。て、当然 , 始まる行が出てくるかしたら , そこて、段落 : cont 中の空白を適当に調整す 「これじゃ俺の美的感覚が納得しない。ちょ ファ Sink : が区切られる。段落は , 追い込みのための るメンバ関数て、すが , そのく適当に > をど っと俺にいじらせろ」 単位となると同時に , 段落の最後の行は行 うするかが問題てす。思いっきて、すが , 行 こうなるわけて、す。 と , 端をそろえない。タブは , 行頭にある場合 の両端から見ていって , 空白が見つかりし そもそも文書整形プログラムの目的は , のみくタブ > として取り扱い , 文中にある だい , 順ぐりに広げていくようにしてみま 見ばえを整えてく美的感覚を納得させる > 場合には単にひとつの空白とみなす。 efo 旧のメインルーチン k クラス 以上のことを踏まえて , プログラムの設 計に入ります。 入力を単語単位て、どんどん行バッフアに 追加していき , 指定された桁数を超過した ら , 行端そろえなどの処理を行ったうえて、 1 行出力する。というのが , 全体のおおまか な流れになるはずて、す。この「行バッファ をめぐる処理』がプログラムの柱て、あり , もっとも煩雑な手続きが要求されるところ て、す。これはクラスにまとめてしまうこと 仮に , このクラスを Sink と名づけるとす ると , メインルーチンは List 1 のようになる て、しよう。 シンプルて、すね。 演算子くくをオーバロードしていますが , この程度の使い方て、あれば , 改めて説明す るまて、もないと思います。別に , 通常のメ ンバ関数呼び出して、あっても構いません。 しかし , このように演算子を使うことによ って , 「読み込んだ行を Sink に流し込む』と いうイメージて、設計されていることが , よ り明確になります。 次に , 肝心の Sink クラスを設計します。 だいたい List 2 のような感じになるて、しよう か。これまた実にシンプルて、す。美しいか もしれません。て、すが , このシンプルなク ラス定義の中にも , すてに根本的な設計ミ List 「フス」 ( こむ ッみこ ~ 一カ ~ 一行流 / / ノ / ノイ 0 00 一 1 よ 2 CO -4 -06 叮 8 9 歹 0 List ク流 ラを さこ ~ ) す「 ~ 〔 ( ノ ト列 ( ( きど】ユ ス字 デ文 大 , 理シ アのの処ッ フ中るラ二れ 」 ~ るフ ッれアえフ一 ( えを / ( フろを。 = ( 一 ( 行アッそア ↓レ ーフい .•.> をフ 用フバをフの ] ~ ー。ネ カッ ( 端ツ。バ 1 。 で一行バ 出バ今行バ + 1 人←》 端″〃登 しが , アな . , 出語 新めフ鼠プ開」り単 .-.. 取ッ 。 , のパ響の字翫 落 ( 頭ブ ( 語 ( 段迂ー行タ o 単 8 」。空 2 れⅡ 0 0 0 ・ 1 【、 1 に響 12 3 4 5 6 7 8 9 加Ⅱ 2 4 6 7 012 3 % % 幻芻 138 C MAGAZINE 1991 11
スタートアップ C 十十 実力養成講座 7 目的を考えながらプログラミングを行いま なおかっ , そこに変数とそれに対する操 ことがありませんが , 実は C 言語の構造体も す。クラスの持つ情報隠蔽という性質をク 作を複合したクラスを用いることて、実際の 問題を簡潔化するために非常に有益な存在 ラスの外部に対してメンバの変数を保護す 問題解決のためのアルゴリズムとその支援 て、す。 るために利用するわけて、す。 のためのアルゴリズムを分離することがて、 今回の問題ても構造体を用いなければ , C 十十て、は , 機能に着目し複数のクラスを き , より簡潔なプログラムを書くことがて、 複数の配列を用いなければなりません。配 用いてひとつのデータ構造を実現するよう きるというわけて、す。 列を用いた場合 , 同期をとるなどの煩雑な にプログラミングします。その結果として このように , 問題の簡潔化のためにクラ 操作が必要になってしまうわけて、す。そう システムの見通しがよい優れたプログラム スを用いる場合には , 構造体の内部の変数 した複雑な操作は , 構造体を用いることて、 になるというわけてす。 を構造体の外部に対して保護するといった 不要になります。 編 白倉伸一郎・山本浩文 送るか , ふたつにひとつ , どちらかの手段 アプリケーション を選ばなくてはなりません。 どちらを選ぶかて、すが , いつものように 方針はく楽なほうへ流れる > ということて、 , 連載もいよいよ後半戦に入ります。実践 「アインシュタイン』 ( 注 : フジテレビて、 編て、は今回から , 簡単なアプリケーション 放映していた深夜番組 ) にならって , まずは ハイフネーションはあっさりあきらめます。 プログラムを作りながら C 十十プログラミン ハイフンは , 単語のどこにて、も挿入て、きる <BASIC TERM> から。 ものて、はなく , ネイテイプて、ないとちょっ グの実際を見ていくこ ①追い込み と感覚がっかめないし , かの Knuth 先生の論 今回題材にとるのは , 英文を対象にした 入力を元の桁数を無視してどんどんバッフ 文を読む気力もないし , ( fCGazette 』誌 一種の文書整形プログラム ( テキストフォー アに追加していく て、も扱っていましたが ) 例外が多くて例題と マッタ ) て、す。 efold と名づけました。 ②折り曲げまたは折りたたみ 文書整形プログラムというのは , 読んて、 と , うだう して適切とはいいがたいし 指定された桁数て、改行して出力する だいい訳をして先に進みます。 字のごとく , 文書を整形して , 見ばえをよ efold は対象を英文に限っていますから , となれば , 行端にかかった単語は次の行 くする働きをするものて、す。たとえば , Fi 折り曲げは微妙な問題をはらんて、しまいま に送ることにしましよう。そうすると , 確 g. 1 のような文書があったとします。 す。つまり , 日本語の文章なら , 禁則処理 かに各行は指定の桁数内に収まります。し この文書て、は , 各行の長さがまちまちに さえしつかり行えば , どこて折り曲げても かし , 行端はそろわずいびつな行が並んて、 なっています。これをそろえて , 見ばえを 構わないわけてす。 よくしてやる , というのが整形の基本てす。 しかし , 英文を相手にするかぎり , しまいます。あちらの雑誌などを眺めてい はそう単純て、はありません。和文が文字単 ると , それて、も別に平気らしいのて、すが , あるいは , 仮に各行の長さがそろっていた としても , それが望みどおりの桁数て、なか 位て構成されているのにひきかえ , 英文て、 日本語て、は何がなんて、も行端をそろえるの った場合に改めてそろえ直すといった , は「単語」が不可分の基本単位となっている が常てす。そういうお国柄に生まれ育った もあるてしよう。文書整形プログラムも高 のて , おいそれと単語の途中て改行を差し 身としては , どうも落ちつきません。 ということて、 , 何とかやりくりして , 行 こむわけにはいかないのてす。 度になると , 本誌 9 月号特集の「フィルタ系 端をそろえることにしましよう。多くの場 無理やり改行して , 分割された単語にハ 言語」と呼べる域にまて、達しかねませんが , 合 , ひとつの行には複数の単語が存在しま 今回の efold て、行うような処理が , すべての イフンをつけて単語の継続を示すか , もし す。その単語の間の空白を行端がうまくそ 基本になるのてはないかと思います。 くは , 単語を分割しないて丸ごと次の行に Fig. 1 サンプルテキスト lt' s easy tO decide on just what kind Of house you want, OW many bedrooms you need, and whether it should have a fireplace or a big backyard. The real challenge is where you are going to get your mortgage, especially if you're a first time home buyer. 曲げ スタートアップ C 十十 137
1991 年 11 月 1 日発行 ( 毎月 1 回 1 日発行 ) 第 3 巻第 11 号通巻 26 号 1990 年 2 月 2 日第 3 種郵便物認可 提携・ LANGUAGE 誌 / 監修・石田晴久 C 言語技術情報誌・ C マカシン 1991 NO 既 6.3 No. 11 980yen 特集 ライプラリ開発技法 ライプラリ自作の基礎知識から実践まで 新連載 >djgcc 詳解講座・ He 0 GCC Wo d 〈第 1 回〉環境変数の設定 巻頭インタビュー >Pa ⅵ Hagerty(fl)NeXTstep 開発ティレクター/ 提携記事 >Get GUI Fast !>An End to the Mysteryof sorting C 言語雑学講座・エンティアン / 明解 AN 引 C 言語入門講座・文字列とポインタ / スタートアップ c 十十・クラスの設計 ・ C98 スーバーライプラリアップテート & Turbo C スモールモテルライプラリ 5 " 2HD ティスク特別付録 ・ GNU awk 日本語対応版・ X68k 版 JPERL ・ XGCC ⑦・本誌掲載ソースプログラム