int addbar(int) ; void f00 : ba 「 : putbar(int int int : addbar(int i) f00 : putbar(getbar( ) 十 i) ; return(getbar()) : : getba 「 (void) f00 : 「 etu 「 n(bar) ; そして , 変数 ba 「の値を利用する関数 , た とえば addbar( ) て、は , 変数 ba 「を直接参照せ すに , getba 「 ( ) によって値を参照し , 関数 こうした方 putba 「 ( ) て、代入するのて、ある。 法は , 実行時に多くの関数呼び出しのオー バヘッドを伴ってしまうのて、あるが , オプ ジェクト指向はあくまて人間のための考え 方て、あり , そのプログラムが少々遅かろう が , それはプログラミング上本質的な間題 とは見なさない。プログラムの見やすさや 信頼性などを中心に考えるのて、ある。なお , SmaIItalk ー 80 て、は , インスタンス変数名と 同じ識別名て、メソッド名をつけることがて、 きるのて、 , 変数とアクセスメソッドの関係 が理解しやすいが , C 十十て、はこれは許され int addbar(int) ; VOid ba 「 (int) : int bar(void) : public : int bar ・ class f00 { ない。そのため , るため , この例のように同一の関数名をつ の形式なども , 関数の識別子として機能す 同じ関数名て、あっても , 引数の型や戻り値 のようには書けない。ただし C 十十の場合 , けることは可能て、ある。 これを , 関数のオーバローディング / 多義 化と呼ぶ。クラスはもちろんのこと , 引数 の型 , 引数の数などが異なれば , 同じ関数 宣言された変数が , 前々回の基本概念の説 が確保されるというわけて、ある。この場合 , ことによって , オプジェクトとしての領域 の型て、あり , ある変数をクラスて、宣言する じて、ある。つまり , クラスはオプジェクト 基本的な原理は , クラスもこの構造体と同 じめてデータとしての領域が確保される。 をそのデータ型て、宣言することにより , は るが , 構造体はデータ型て、あり , ある変数 このようにクラスは定義されるわけて、あ といえる。 は情報隠蔽の機能をもたないクラスて、ある になるわけて、ある。 C 十十においては , struct すべてのメンバが public なもの , ということ を定義することも可能て、あるが , その場合 , ワード struct を用いて , C 言語と同じ構造体 はいうまて、もない。また , C 十十て、は , キー て、あるが , その構造体のスコープに従うの ープをもたす , どこからも参照されるわけ をもったメンバ変数は , クラス単位のスコ トの外部スコープに該当する。 public の属性 に該当するものて、あり , public がオプジェク スコープが , オプジェクトの内部スコープ から導入された機能て、ある。以ふたつの 関する記述がなく , この後の C 十十 Ver. 1.1 esley , 1986 ) があるが , これには protected に Programming LanguageJ (Addison ー W 位置づけにあるといわれている TThe C 十十 十」以外に , C 言語における K& R のような 触れている 1987 年の「 An overview of C 十 C 十十に関する論文には , その概要について る。なお , Bjarne Stroustrup の丁・になる ス階層に該当する ) から参照て、きるものて、あ 数と , そのクラスの派生クラス ( これがクラ 次に protected は , そのクラスのメンバ、関 て、あるといえる。 Smalltalk-80 よりも高度なポリモフィズム 名を用いることがて、きるのて、ある。これは , わジェクト指向 への招待 明て、述べたインスタンスにあたる。 C 十十の場合 , クラスはオプジェクト ( イ ンスタンス ) のて、あり , オプジェクトて、は こが , SmaIItaIk-80 と C 十十のオプ ない オプジェクト指向への招待 79 は , 県 = クラス , 変数 = オプジェクトとい ンス変数と呼ばれることもない。 C 十十て、 される変数はメンバ変数となり , インスタ とは呼ばれない また , クラス内部の隠蔽 とて、 , クラス変数と呼ばれ , インスタンス 変数は , クラスに従った型の変数というこ ことて、あり , 型のことは意味しない。その す C 十十て、は , オプジェクトとはある変数の 以 l•., クラスをオプジェクトの型とみな 性があるといわれている。 には SmaIItaIk ー 80 のほうカ℃十十よりも適合 常に柔軟に行えるのて、 , プロトタイプなど まったくないため , プログラムの記述が非 則もって変数を宣言しておく必要が ということも可能になるのて、ある。このよ スの領域を確保してから , 変数に代入する 縛する変数は必要がなくなる。インスタン ェクトとする場合 , そのインスタンスを束 を生成することになるが , クラスをオプジ 合には , 変数の宣言によってインスタンス 生成と呼ぶ。そして , クラスを型とする場 て、きるのて、ある。これを , インスタンスの 山にオプジェクトの領域を確保することが まり , プログラムの中て、必要なときに , 自 式によって行うことがて、きるのて、ある。っ その変数の宣言にあたることをメッセーシ ろが , クラスをオプジェクトにした場合 , の宣言文が出現してきた時点て、ある。とこ ミングは , プログラムの流れにおいて , そ って宣言した場合に , 領域を確保するタイ いえば , たとえば , ある変数をクラスによ れは具体的にどういうことを意味するかと ェクトとして情報の隠、蔽を行っでいる。 ある。 Smalltalk-80 は , クラスをもオプジ オプジェクトとみなすことはて、きないのて、 の E 位圧換を保っているため , 型の概念を る点て、ある。あくまて、 , C 十十は , C 言語と ジェクトに関して , もっとも大きく相違す
アルゴリズム ァータ構造入門 Fig. 9 子をひとつもつ節の削除 「 00t 肖」除のプロク、ラム 削除を行う関数 delete を List 3 に示しま す。この関数は , いましがた説明した削除 の手順をそのままコーディングしたものて、 す。挿入の関数 inse 「 t と同様に , 注目してい る節へのポインタて、はなく , その親へのポ インタを使っています。 NODE * * 型の変 数 p が , この役目をはたしています。 6 行目 ( a ) から要素 9 を削除すると ( b ) になる の wh ⅱ e ループて、削除対象となる要素を探索 ( a ) ては変数 p は変数 root を指していることに注意 ( これは境界条件になっている ) します。実際の削除の処理は 8 ~ 21 行目て、行 の操作と , Fig. 10 の① ~ ③のポインタが対 は木の枝ぶりによって大きく変化します。 っています。 応しています。また , 20 行目て、は , 標準ラ 直感的に考えて , 木が低くて枝分かれして 8 行目を実行した時点て、は , 変数 p は削除 イプラリ関数 f 「 ee によって取り除いた節をシ いるほうが探素は高速になります。 Fig. 11 される節の親 ( のメンノヾ left または right) を , ステムに返しています。 (a), ( b ) のふたっ二分探索木を比較してみま 変数 x は削除される節そのものを指していま す。 9 行目はひとつも子をもっていない ( 葉 しよう。どちらの木も , 1 から 7 まて、の 7 つの 要素をもっています。 て、ある ) 場合て、す。実際には Fig. 8 のように Fig. 11 ( a ) のように , 根からすべての葉ま ポインタが書き換えられます。 12 行目は右 二分探索木の特長について考察してみま て、の経路の長さが等しいような二分木を完 の子のみを , 14 行目は左の子のみをもつ場 しよう。二分探索木て、は , 探索 , 挿入 , 削 全ニ分木 ( comp 厄 te binary tree) と呼びま 合て、す ( Fig. 9 ) 。最後に , 16 ~ 18 行目が , 左 除の 3 つのどの操作についても , 根から始め す。実際には , 完全二分木を作ることがて、 右ふたつの子をもっ場合の処理て、す ( Fig. て木をたどって , しかるべき位置を探し出 きるのは , 節の数が 2 1 個 ( n : 自然数 ) て、ある こて、 , 関数 de 回 emin を呼び出して 10 ) 。 します。つまり , 挿入 , 削除を行うにも , ときに限られていますが , 制限をゆるめて , いますが , これは「部分木から最小の要素を 探索を行って挿入する位置 , 削除の対象を 根からもっとも遠い節まて、の経路の長さと , 取り去る」ためのものて、す。関数 deletemin 決めなければなりません。て、すから , 二分 根からもっとも近い節まて、の経路の長さと は List 4 のように定義されていて , 部分木か 探索木の処理の計算量は , 探索の計算量に の差が 1 以ドて、あるような二分木を ( 広い意 ら最小の要素を探し出して部分木から取り 味て、の ) 完全二分木と定義します。 n 個の要 帰結されると考えることがて、きます。 除きます。そして , 取り節いた節へのポイ 先ほども考察したように , 探索の計算量 素をもつ完全二分木は , 根から節への経路 ンタを値として返します。 List 3 の (1) ~ ( 3 ) Fig. IO 子をふたつもつ節の削除 「 00t 「 00t つ f 「 ee される (a) ニ分探索木の特長 9 9 「 0 Ot 5 3 7 1 7 1 free される 8 3 8 ① ② ( a ) から要素 5 を削除すると ( b ) になる ( a ) ては変数 p は要素 9 のメンノ e 負を指している 要素 5 の右部分木中の最小の要素 3 が要素 5 があった位置へ移される 丸数字のポインタは List 3 の丸数字で示される文に対応している (a) (b) アルゴリズムとデータ構造入門 75
う意味になるのてある。 きだけを用いて変数操作を行うというクラ いコマンドなどが入力される可能性がある。 スの機構は , メニューによる計算機の操作 内部の変数は , 絶えず誤操作の可能性にさ と対比して考えると非常に理解がしやすい らされてしまうのて、ある。ところが , メニ メニュー操作の代表として , 第一回にはマ ューによれば , そのオプジェクトに対して ルチウインドウの例を示したわけて、あるが , 行われる操作は , そのメニュー項目に列挙 以上述べたように , クラスのメンバ変数 Fig. 1 に示したように , メニューとして表示 されているものに限定される。なおかっ , されている項目は , そのウインドウがその にはスコープによって大きく 3 つのレベルが メニューは具体的な操作を示すものて、はな 状態て、 , 操作を受け付けられる一覧と考え く , 操作の呼び出しを示すものて、ある。そ ある。クラスの外部 , つまりクラスのユー ることがて、きる。移動 / m 。 ve の操作は原点 ザは , 公開部のメンバしか参照することは して , それらの操作が保証されたものとす て、きない。非公開部の変数の参照手続きを の値を変えることて、あり , 大きさを変える ると , 内部変数は完全に保護されるのて、あ frame 操作は , 高さと幅の値を変えることて、 公開部に記述しておくことにより , 非公開 る。これはまさに , クラスの公開部の機能 もある。つまり , ウインドウをオプジェク 部の変数は特定の手段によってしか参照さ と同じて、ある。 Fig. 1 に示すように公開部に トと考えた場合 , メニューは各内部変数へ れることがなくなるのて、ある。公開部に定 はそのオプジェクトに対して操作て、きる項 義されている非公開部のメンバに対する参 の操作としてとらえることがて、きる。 目の , いわばメニューが列挙されているの 照手続きが , 完全に信頼て、きるものて、あっ こうしたウインドウ操作がメニュー形式 て、ある。そして , そのオプジェクトの外部 たとするならば , クラスの内部の変数に対 て、はなく ,UNIX や MS-DOS などのキャラ からは , そのメニューを選ぶことしかてき する参照も , 完全に信頼て、きるものとなり , ない。そのオプジェクトが , どのような変 クタベースの OS のように , コマンド文を入 不正な参照から変数は保護されるというこ 力するような機構て、あったとしたらどうて、 数を非公開部にもつかすらわからないのて、 とになるのて、ある。 あろうか ? ある。 このような公開部に限定されている手続 ミスタイプの可能性や , このように公開部はモジュールのインタ 受け付けられな Fig. 1 マルチウインドウメニュー例 公開部の機能と メッセージ クラス 表示内容 枠の幅 View の名前 char name[10]; int offsetx; int offsety; int height; intwidth; int border; This is a sarnple of Vie•w 座標位置 under frame CQlla se c10Sé move void under(void); voi&move(int, int); void frame(int, int); void collapse(int, int); void close(void); 幅 移動する , 大きさを変える , アイコンにする , 閉じるなど 80 CMAGAZINE 1991 1
ルボート 最新 開第一環ー境 字列の検索 / 置換 , カーソルジャンプなど , 機能 ログラム中の p 「 intf 関数などによる出力が表示さ 面て、も Mifes の 80 % 近くをもっており十分といえ れる。 もうひとつの強力なデバッグ機能は不法なメ このエデイタては , 最大 10 個まて、のファイル モリアクセスが自動的に監視されることて、あ が取り扱える。これはすなわち , インタブリタ る。ほかのプログラミング言語に比べメモリア て、実行て、きるプログラムが 10 個まて、オンメモリ クセスの自由度の高い C て、は , バグによるメモリ て、もてるということて , BASIC て、 1 度にひとっ 内容の破壊 , 暴走に陥ることがよくある。 のプログラムしか編集 / 実行て、きなかったのに比 ORCHIDC て、は配列として宣言した変数につい べたいへん便利になっている。プログラムは ての領域チェックだけて、なく , malloc や calloc HOME キーを使って切り換える。 といった関数て、確保した領域についても不法な アクセスをしていないかチェックしてくれる。 したテパッグ機能 80286 , 80386 といったメモリプロテクト機能を もった CPU なら容易て、も , 8086 ( あるいはその互 Turbo C など統合環境化コンパイラの特長の 換モード ) て、動作する DOS 環境下て、は , 実現の難 ひとつに環境内にデバッガまて、組み込まれてい しい機能て、ある。 る点が挙げられる。これにより , 環境から抜け ただし , これも未穫得のヒープ領域に対して 出さずにランチェックして , すばやくプログラ が精いつばいのようて、 , malloc したのち free し ム中の問題箇所にアクセスて、き , 開発のターン たエリアへのアクセスなどはチェックしきれな アラウンドタイムを短縮て、きる利点がある。 ORCHIDC は , 1 行ずつ実行するインタブリ この他デバッグ機能としては , プレークポイ ントの設定 , ウォッチ変数の指定などがある。 タて、ある点を生かしトレース , シングルステッ プ実行といったデバッグに役立つ機能が用意さ これらはエディットモードて、ソースプログラム を見ながら指定て、きる。 Fig. 4 はプレークポイ れている。 Fig. 3 はプログラムをシングルステ ップ実行しているときの画面てある。いちばん ントを設定したところて、ある。プレークポイン トは行ごとに設定て、きる。まず設定したい行に 上の 1 行はガイドラインになっている。 カーソルを移動し , そこてを押してプレー ファイル名と実行中の行番号が表示される。そ クポイントメニューを開きプレークポイントの の下はプログラム表示工リアになっている。 設定″を選ぶ。するとその行は水色て、表示されア こにはソースプログラムの実行中の部分が表示 される。とくに現在実行中の行は黄色て、反転表 ンダーラインがついて設定されたことが示され 示される。 ウォッチ変数の指定は , ーキーを押し ORCHIDC がほかの処理系のデバッガに比べ ながら亘を押してウォッチメニューを開き , て優れているのは次の解説工リアてある。画面 ウォッチ変数の設定″を選ぶ。すると Fig. 5 の のまん中あたりに表示される解説工リアには , 現在実行中の行についての解説が表示される。 ようにウインドウが開いて , 左側のウインドウ に変数の一覧が現れる。この中から任意の変数 たとえば実行中の行に計算式が含まれていれ を選ぶと , 右のウインドウに選ばれた変数が表 ば , その演算結果が表示される。また for 行など 示される。選択された変数はシングルステップ の場合には , ループカウンタの値や判定式の判 実行時などにはウォッチウインドウが開いて随 定結果を表示する。ウォッチ変数の設定により 時その値の推移を監視することがて、きる。 各変数の値を調べるだけてなく , 真偽の判定を Fig. 5 ウォッチ変数の設定 また , プログラムの実行方法としても , 通常 含めた状態が表示されるのてデバッグにはあり の連続実行や前述のシングルステップ実行のほ がたい。さらに , その変数の型 ( int か doub か かに , 現在の実行している行やウォッチ変数を といったデータ型 ) を示してくれる。いちばん下 の広い部分は実行結果表示ェリアと呼ばれ , プ 表示しながら実行するトレース実行がある。さ 3 日物 : い田ゞ ! 0 第れ朝 ! お朝 / ・メモリフ C ックを 03 い 0 : で確保・ム Fig. 2 工ディットモード Fig. 3 シングルステップの実行 山をやイード フ一クプ」 / ノレプし方「 ・ - ・メモリプこックを 0 引に 0 て確保・ Fig. 4 プレークポイントの設定 最新開発環境レポート 141
C 十十の オプジェクト まず , オプジェクト指向て、あるためには , 何はともあれ , オプジェクトを記述するこ 前々回述べ とが可能て、なければならない もっとも広い意味のオプジェク たように ト指向県プログラミング言語は , 少なくと も Object-Based として , 現実世界の直接的 な記述がて、きるような言語を指すが , そこ にいうオプジェクトは , 情報隠蔽の機能を もった記憶領域と手続きの融合体と定義さ れる。オプジェクトとして重要なことは , 1 記憶領域と手続きの融合体であるこ と 2 情報隠蔽機能をもっこと のふたって、ある。 C 言語においては , 記憶領 域と丁・続きの融合体は , 前述したように 関数へのポインタをメンバとしてもつ構造 体によって実現することがて、きる。ただし その構造体は , メンバに対する情報隠蔽の 機能をもっていない。構造体を基準にして 変数や関数のスコープを設定することはて、 きないのて、ある。この構造体にスコープを 設定することによって , C 十十は情報隠蔽機 能をもった特殊な構造体を定義て、きるよう にしている。 C 十十て、はこれをクラスと呼 び , 定義の基本形式は , 構造体におけるキ ーワード struct を class に変えたものて、ある。 このクラスについて , Bjarne Stroustrup の前述の論文からまとめてみると , まず , クラスはメンバの集合体て、あり , メンバは , 変数か関数のどちらかて、ある。 C 言語て、は , 関数へのポインタを構造体のメンバとする ことはて、きるが , 関数自身をメンバとする ことはて、きなかった。しかし , C 十十て、はそ れが可能となっている。これは , 「オプジェ クト = 手続き十記憶領域」ということを明確 に示すためて、あり , 上述したオプジェクト としてのポイントの 1 をこれにより実現して 78 CMAGAZINE 1991 1 2 の情報隠蔽機能は , 構造体の個々のメン バに対してデータ県以外に , スコープ ( 可視 性 ) の属性をもたせることにより実現されて いる。 C 言語て、は , 構造体はあくまて、ひとつ のデータとしてひとつのスコープしかもて ず , メンバは構造体自身と同じスコープを もっことになるが , C 十十のクラスて、は , 各 メンバごとにスコープをもてるのて、ある。 前述したように , オプジェクトが情報隠蔽 機能をもっことによって作られるスコープ は , ファイルや関数を基準にしたものて、は なく , クラスを基準に設定され , 大きくク ラスの内と外というふたつのレベルを設け ることを目的としたものて、ある。 C 十十の場 合 , クラスのスコープには , private, protected, public の 3 つのレベルがあり , ク ラスのメンバは , それらのうちのいずれか をとることになる。そして , それらの属性 は , 構造体自身に記述され , 基本形式は以 下のようになる。 class クラス名 { 非公開 ( p 「ⅳ ate ) 部 protected ・ 非公開 (protected) 部 public : 公 WJ(public) 部 おのおのについて説明すると , private の 属性をもつメンバは , そのクラスのメンバ として定義される関数 ( これをメンバ関数と 呼ぶ ) からしか参照て、きない。この属性が , 情報隠蔽を実現するものて、ある。なおメン バ関数は , メンバ変数に対して直接参照て、 きるわけて、あるが , オプジェクト指向流に プログラミングしていくと , しばしばこの メンバ関数群て、すら変数のスコープとして は広すぎると感じることがある。 そうした場合に , Smalltalk ー 80 のプログ ラミングて、慣例的に用いられている手法を こて、紹介する。 Smalltalk ー 80 て、は , ある インスタンス変数をもっクラスを定義した 場合 , その変数に対する参照と値の代入の ふたつのメソッドを無条件て、同時に定義し てしまう。そして , ほかのメソッドがイン スタンス変数を参照したり , 代入したりす る場合 , 必ずそのふたつのメソッドを経山 して行うのて、ある。当然その変数は自分の スコープの中にあるため , 直接操作がて、き るのて、あるが , それは行わずにふたつの丁・ 続きを通して , 間接的に変数参照を行う。 そして , インスタンス変数のスコープを , すべてのメソッド群から , ふたつの丁・続き に限定したものと仮定してしまうのて、ある。 SmaIItaIk-80 は , メソッドをメソッドカテ ゴリという観点から , おおまかにグルーヒ。 ングすることがて、きる。そして , このふた つのメソッドには , 変数の参照を行うとい った愚味合いて、 , 「 accessing 」というカテゴ リ名がつけられる。このカテゴリという概 念は , C 十十にはまったくない機能て、ある。 なお , このカテゴリは , Smalltalk-80 のロ Ⅲ : 様には含まれず , プログラミングの慣 例にまかされている。この丁・法により , のメソッドに対しても , 変数への参照経路 を限定することがて、きるため , 不用意に変 数の破壊や誤操作がされる恐れがなくなり , より信頼性の高いプログラム記述が行える のて、ある。また , メソッドのデバッグ効率 も非常に高いものとすることがて、きる。 そのオプジェクトとメソッド群が , 正し く機能しているかどうかは , その変数への 参照 , 代入のふたつのメソッドにプレーク ポイントを入れることにより容易に判断す ることがて、きるのて、ある。これを C 十十に応 用してみると , たとえば以 - ドのように , 変 数 ba 「をもつクラス f00 を定義した場合 , メ ンバ関数として , 参照と代入のふたつの手 続きを , おのおの getbar( ) , putba 「 ( ) とし て無条件に定義してしまう。 class f00 { int bar : public : int getbar(void) ; void putbar(int) :
合は前々回述べたように , クラス階層によ バ単位て、行われる。そのため , 基本クラス と導出クラスの間て、スコープを変えたり , って , 丁・続きは追加か変更が行われるが , 変数は追加されるだけて、ある。つまりド位 制御を行うことがて、きるのて、ある。キーワ のクラスて、 , インスタンス変数を定義しな ード「 public 」を用いずに , 先ほど示したよう な形式て、定義すると , 基本クラスのパプリ おすことはて、きないが , メソッドは同じ識 別名 ( セレクタ ) て、再定義しなおすことがて、 ック部のメンバは , すべて導出クラスのプ ライベート部に継承され , 後のようにキー きるのて、ある。変数の追加のために行うの ワード「 public 」を用いると , 導出クラスのパ を機能継承 , そして手続きの追加 , 変史の ために行うのを構造継承と呼ぶこともある プリック部に継承される。ところが , プラ イベート部の情報に関して , それを参照て、 が , あまり一般性のある用語てはないよう きるのは原則としてそのクラスのメンバ関 て、ある。 数に限定されるため , 導出クラスのメンバ メソッドに同じ識別發をつけてド位のク 関数からは , 基本クラスのプライベート部 ラスて、変史することをメソッドのオーバラ イドと呼ぶ。これはオプジェクト指向重 に対して直接参照がて、きない 要な概念て、ある。この機能により , クラス そこて、導出クラスからは , プライベート 階層間て、も同じような機能の丁・続きには , 部に対しては , 基本クラスのメンバ関数を 経山する必要がある。このように , C 十十て、 同じ識別名をワえることがて、きるため , ポ リモフィズムがより高度に実現て、きるのて、 は導出クラスと基本クラスは , 別々なクラ ある。 C 十十の場合は , この階層関係にメン スとしての意味合いが強いのて、あるが , バのスコープが関係してくるため , 若「複 こて意味をもってくるのが , もうひとつの スコープて、ある protected て、ある。 protected 雑て、ある。 C 十十て、は , 変数も関数もクラス は , 「あるメンバをクラス階層中にない関数 のメンバて、あることには相違点がないため , についてはプライベートにしたいが , 派生 原則として導出クラスにおいては , 関数も 変数も追加はもちろんのこと , 変更も自山 クラスのメンバ関数からは , その属するク この導出クラス textview にも , 基本 ) レタ犬 ラスのメンバにアクセスする場合と同様に に行うことがて、きる。導出クラスにおいて , ラ = と同じ別名て、 , メ , 、、関数 disp は ) 同じ識別發をもつメンバを再定義した場合 , アクセスが可能なようにしたい ()n Over を含むことカて、きる。この導出クラスは , 基本クラスに変数を追加したものて、あり , view of C 十十より ) 」場合 , クラスの外部に 基本クラスの継承されたメンバは , オーバ 基本クラスの変数にはまったく変更を加え 対しては非公開部て、あるが , 導出クラスの ライドされてしまうわけて、ある。ただし , 基本クラスにおけるそのメンノヾは , 参照の ずに , クラスを定義している。導出クラス メンバ関数からは , 直接参照てきる属性を 順番が導出クラスのメンバよりも後になる のメンバ関数 disp y ( ) は , 新たに追加した もつものが基本クラスのプロテクテッドメ 変数 contents を操作するために , 定義され ように , 優先順位が設定されるだけて、 , 消 ンバなのて、ある。 C 十十はこれら導出の機能 しかし , この優 る関数て、あるといえる。なお , 導出クラス 滅してしまうのて、はない によって , 次々と新しいクラスの定義をし のメンバ関数は , 関数名の前にその関数が 先順位を変史したほうが都合のよい場合も てプログラミングしていく。この基本クラ 属するクラス名と「 : : 」を書いて宣言する。 スと導出クラスが , オプジェクト指向にお ある。 こうした導出関係の場合 , 基本クラスのプ ける階層関係て、あると考えることがて、きる たとえば , 以ドに示すように , メンバ変 ライベートなメンバ変数 width や height を操 数に height, width など , メンノヾ関数に dis のて、ある。 作するには , 基本クラス自身のメンバ関数 オプジェクトは , 変数と手続きのふたっ p Y ( ) などをもっ基本クラス baseview を宣 の情報を保持している。クラス階層を設定 言する。次に , その導出クラスとして , メ を呼ぶことになる。 することによって , 階層間においてそれら ンノヾ変数 contents をもつ textview を宣言した ところが , 関数 display() は , 導出クラス て、オーバライドしているため , 導出クラス の情報を継承して共有することのほかに とする。 class baseview { 追加するか , 変史するかのふたつのことし textview の関数 display( ) の宣言中て、 , dis p は y ( ) を呼んだ場合 , それは導出クラス自身 ことがて、きない。 Smalltalk-80 の場 か行う int height ; int weidth : public : VOid display( ) ; : public baseview class textview contentsC500] : Cha 「 public : VOid display( ) ; VOid textview : : display( ) baseview : : display( ) ; 82 CMAGAZINE 1991 1
ます。て、すから , (a) のように低くて十分に 枝分かれした二分探索木のほうが好ましい ことになります。 - 入のプロク、ラム 挿入の手順を C 言語て、記述したものが List 2 て、す。要素を挿入する場所を決める手順自 体は , 探索とまったく同じて、すが , コーデ イング上て、は , 現在注目中の節の表現法が 少し変わっています。つまり , 探索て、は注 目する節へのポインタを使って二分探索木 をたどっていましたが , 挿入て、は注目する 節へのポインタが入っている場所へのポイ ンタ , つまり親 ( のメンバ right または left) へ のポインタを使うようにしています。なぜ なら , 節を挿入するには , 親のメンバ left か right を変更して , その節を指すようにしな ければならないからて、す。これは , 連結リ ストにおいて , 要素を挿入するのにひとつ 前のセルへのポインタが必要になるのと似 ています。 関数 search(List 1 ) て、は変数 p は NODE * 型て、したが , 関数 insert(List 2 ) て、は変数 p は NODE * * 型になっています。 まず 5 行目て、変数 p が変数 root を指すよう に初期化します。次に , 6 行目の wh ⅱ e ループ を要素を挿入すべき場所が見つかるまて、繰 り返します。そして , 挿入すべき場所が見 つかったら , 15 行目以降が実行されます。 まず , 15 行目て、 , 標準関数 ma Ⅱ oc を使っ Fig. 3 ニ分探索木への挿入 Fig. 4 子をもたない節 ( つまり葉 ) の削除 1 9 5 7 (a ) 要素 1 を削除 ( a ) から要素 1 を削除すると ( b ) になる Fig. 5 子をひとつもつ節の削除 9 14 1 9 5 7 (b) 9 3 4 1 5 3 4 要素 5 を削除 (a) ( a ) から要素 5 を削除すると ( b ) になる。要素 5 があった場所に要素 3 を移動する て , 節を割り当てます。新しい節には子が ないのて、 , メンバ right と left には NULL を入 れておきます。 19 行目て、要素の値をセット してから , 20 行Ⅱて、節を正しい位置に挿入 します。 この時点て、 , ポインタ p は , 挿入され . る節 へのポインタが入っている場所を指してい ます。 2 (b) たとえば , Fig. 3 ( a ) の二分探索木に要素 10 を挿入することを考えましよう。この木 に対して insert ( 10 ) を実行したとき , while ループを抜けた時点て、は , ポインタ p は Fig. 3 ( b ) のように節 4 のメンバ right を指していま す。 20 行目て、は図中の点線部分のポインタ が連結されて , 要素 10 が挿入されます。 1 2 4 3 (a) ( a ) の木に要素 10 を挿入する 72 CMAGAZINE 1991 1 data left ri ht
フェイスに相当し , 内部の情報を隠蔽して 信頼性の高いプログラムを記述することが て、きるのて、ある。しかし , こうしたメニュ ー的なインタフェイスの利点は , これだけ にとどまらない。マルチウインドウよりも , もっと馴染み深いものとして , R PG の画面 形式をあげることがて、きる。最近のファミ リーコンヒ。ュータなどに見る R PG のほとん どすべてがこのメニュー形式をとっている。 たとえば , これがコマンド形式て、操作を するとしたらどうて、あろうか。まず , 子供 たちは多くのコマンドを覚えきれすに , 冒 険の目的を達成て、きないのて、はないだろう か。メニューによってオペレーションを容 易にすることもて、きるのて、ある。オプジェ クトが , 自分の可能な操作を列挙している ことによって , オプジェクトの外からはそ のオプジェクトに対する操作を覚える必要 がなく選択していけばよいということにな り , プログラム記述が , メニュー操作のよ うに簡単なものになる。そのうえ , プログ ラム中にオプジェクトを明記することによ って , 第一回に述べたポリモフィズム ( 多 形 , 多態 ) を利用することがて、きるのて、 , オ プジェクトの利用が , つまりプログラムの 作成が非常に簡単になるのて、ある。 あるオプジェクトがどのようなことを行 うのかについては , その公開部のメニュー 項目がそのすべてて、あり , ほかのオプジェ クトて、あっても , 同じような操作には , ポ リモフィズムによって , 同じ名前をつける ことがて、きるのて、 , オプジェクトの再利用 などにも応用が可能になるのて、ある。 以上がクラスのもっ機能て、ある。 C 十十が もつオプジェクト指向のためのメカニズム は , 実はこのクラス概念しかないといって も過言て、はない。 C 十十を拡張 C 言語て、はな く , オプジェクト指向型言語の観点から見 た場合 , 非常に簡潔なプログラミング言語 て、あるといえようか。 それて、は , このクラス概念を中心にして , それ以外のオプジェクト指向にまつわるい くつかの概念が , どのように実現されてい るかを見ていくことにする。 オプジェクトに対して外部から可能な唯 ーの操作は , メッセージを送ってそのオプ ジェクトの内部手続きの呼び出しを要求す ることて、あった。オプジェクトがクラスと いう構造体てあるならば , 構造体に対して 可能な操作は , そのメンバに対して「 . 」か 「一 > 」演算子によって参照することて、ある。 つまり , オプジェクトに対する手続きの呼 び出しの要求て、あるメッセージは , クラス の場合には構造体のメンバとして公開部に 定義されているメンノヾ関数へのアクセスが 該当するのてある。繰り返しになるが , の構造体メンバに対する参照は , 「クラス変 数 . メンバ」または , 「 ( & クラス変数 ) ー > メ ンバ」という形式をとるが , これはオプジェ クト指向プログラムの表現形式て、あるメッ セージ式の「 Object message : arg 」と同じ 形式をとっているのて、 , 対比するのは容易 て、あろう。メニューのように , 公開部に列 挙されているメンバ関数がセレクタて、あり , このメッセージによって呼び出されるメソ ッドがメンバ関数の実装ということになる。 ただし C 十十にはメッセージ , メソッドなど の特別な呼称はなく , あくまて、特殊な構造 体のメンバへの参照なのて、ある。 このようにクラスとメッセージは特殊な 構造体とそのメンバに対する参照として実 現される。それて、は , クラスの階層はどう て、あろうか。 クラスがオプジェクトの型て、あるとする と , いくつかの似た型の間て、 , 継承による 情報共有への要求が生じてくる。これに対 して C 十十て、は , 派生クラスまたは導出クラ スと呼ばれる機構と , メンバのスコープ protected によって , この型階層を実現して いる。オプジェクトは , 変数とそれらに対 C 十十の クラス階層 わジェクト指向 への招待 する操作手続きを内部情報として保持して いる。新たにクラスを定義する場合 , 既存 のクラスとの差分のみを記述することて、 , それを可能とする機構が継承て、ある。継承 は , クラスのもつ変数と , 手続きのふたっ を再利用するために行われる。 C 十十て、は , あるクラスて、定義されている変数や関数の メンバを利用するために , 新たなメンバを もつクラスを付け加えて , 新しいクラスを 定義することが可能てある。このようにし て定義されたクラスを派生クラスまたは導 出クラス (derived class) と呼び , もとにな ったクラスを基本クラス (base class) と呼 ぶ。このクラスの導出の記述形式は以下に 示すように , キーワード public を用いるもの と , 用いないものの 2 種類がある。 class class クラス名 : 基本クラズ名 基本クラスの変更 , 追加など クラス名 : pu blic 基本クラス名 これらは , 導出クラスから基本クラスの パプリックなメンバにどのようなスコープ を設定するか , ということに基づく違いて ある。前述したように , オプジェクト指向 の世界て、は , スコープは原則としてオプジ ェクト単位て、設定され , オプジェクトの内 部か外部かのスコープしか必要て、はない たとえば , Smalltalk ー 80 ては , サプクラッ シングによってクラスを追加した場合 , ス ーパークラスのもつ情報は , 無条件て、下位 のクラスに継承され , その結果として , ス ーパークラスの内部情報は , すべてサプク ラスのスコープの範囲にあり , 新たに追加 されるクラスて定義される変数やメソッド と , 既存のクラスに定義されている変数や メソッドとを区別することなく使用てきる。 しかし C 十十の場合 , スコープの設定はメン オプジェクト指向への招待 81
三田典玄の 実践 C プログラマ 養成言 第回「 NULL 」の研究 p [0] グて、は正確さに欠けるものの , とにかく確 実に動く。ところが , int のデフォルトが 16 p [ 2 ] = N U LL ; ビットの処理系て、はどうだろうか ? とく と書く人がある。 NULL はポインタ値とし に最近の MS ー DOS / 8086CPU 上の処理系て、 て使う , という暗黙の了解を無視している というようなコーディングをするクセをつ はいろいろなメモリモデルをサポートして ことはまあよいとしても , 「文字列のおわり」 けて , 対処するわけて、ある。 いるから , たとえばデータ領域のポインタ を示す「文字変数 < 値 > 」として「 NULL 」を 変数型は 32 ビットて、 int はあいかわらず 16 ビ 使ってはいけない。やはり正確には , ノトだ , などということはたくさんある。 p [ 2 ] = (char)0 ; MS-C などてたんにラージモデルの指定をし たときなどがこういった状況て、ある。 p [ 2 ] NULL はもともと C 言語ライプラリのポイ この状況て、はたとえば , ンタ変数のある特定な意味をもつ値 , とい 前述のよう と書くべきだろう。もちろん , if(NULL = = ()p = fopen(.. うことて、考えられた。まずありえない CPU に文字定数は int 型なのだから , 本当は というコーディングて、は明らかに処理系が p [ 2 ] 内「アドレス」ということて、この値が採用さ 自動的にキャスティングをしてしまうから , こまて、書くとなんだ れたわけだが , あくまて、もそれが定義され , となる。もっとも , fp に fopen() 関数から値が入るときには 32 ビ 意味をもっているライプラリ関数にかぎっ か重箱の隅をつついては嫁いじめに精を出 ットのままだが ,fp と NULL の比較ては NUL す向田邦子の小説に出てくる強欲ババアみ て使うことになる。 L が 16 ビットから 32 ビットに拡張され , か たいて、あんまり書いているほうも気分がよ 現実的に私は制御関係の C 言語ソースリス っ型が変換されて比較が行われる。だから , トて、 , この N ULL を使わないソースリスト 正確にいうとよくないということになる。 くなし さらにこういったようなまちがいをした にはいって、もお目にかかる。たとえば lntel 最近て、はあまり見かけなくなったが , 昔 の PL/M とか Pascal とリンクされたり , イン ことはないだろうか ? の処理系て、は型の違うふたつの整数変数や タフェイスされる可能性のある C 言語ソース 整数の値をもつ式の比較や演算がすべて int Cha 「 リストて、は , 文字列の表現力℃言語の標準的 Cha 「 にキャストされてから行われる , というと なライプラリとは違うことがあるのて、 , NULL んて、もないものもあった。 Cha 「 が意味をなさないこともある。 こういった処理系て、は当然 fp て、入ってくる しかし , そうはいっても C 言語のおまけの 32 ビットの値の上位が切り捨てられ , 16 ビ ライプラリを使うかぎりこの NULL とはつ ットに整形された後 , 16 ビットの int の定数 きあっていく必要があるだろう。 て、ある NULL と比較される。このような場 とくに配列て、はそうて、ある。よく使うの 合だと , 32 ビットの下位 16 ビットは 0 だが , がポインタ変数の配列て、ある。ポインタ変 1 : 位 16 ビットは 0 て、ない値が返ることもある 数を配列にしたとき , その「おわり」を示す わけだから , 「エラーて、ないものがエラーと のにこの NULL が使われる。 して判断されるときもある」というおかしな Char ことになる。 'data", p 「 ogram', 'buggs", というわけて , どの処理系て、も同じよう にこの式が働くように #include <stdio. h> あるいは #include <stdio. h> extern FILE *fopen() ; if((FlLE *)NULL = (fopen(filename,"r"))) とか , s [20] ; 'Source Strings" if(x) else p = (char *)NULL ; strncpy(p,r,3) ; se のところて、本人はポインタをクリアし た = 文字列をカラにしたつもりなのだが , やはりよく見るとだめだ。ほんとうは else ” fOOl' ' NULL} : 上記のようなコーディングはよくやるだろ よくまちがえるのに ところが , p [ 3 ] : Cha 「 というわけだ。 としたかった , 文字列のために確保されている領域をカ
タンスの牛成が , すべてメッセージ式て、行 われるということて、ある。 C 十十て、は , オ プジェクトの定義は , クラスの宣言て、行い インスタンスの生成は , 変数の宣言て、行う。 それは , 処理のプロセスを記述するプログ ラムとは別のものて、あり , 実際のプログラ ムは , 関数 main ( ) から開始する。これは , 標準 C 言語とまったくかわることがない。極 Fig. 3 テータ構造と CO ⅱ ections の階層 論をすれば , C 十十て、オプジェクト指向が活 躍するのは , この関数 main ( ) から開始する メッセージ式が使える部分だけなのて、ある。 ところが SmaIItaIk ー 80 て、は , これらの宣言 もすべてメッセージ式て、行われる。とくに クラスの宣言に関しては , 編集時にそのた めのメッセージ式を , 実行まてしてしまう のて、ある。たとえば , クラス SomeOne に いいえ サプクラスとしてインスタンス変数 name を もっクラス NoOne を追加するとする (C 十十 て、いえば , 基本クラス some 〇 ne から , 派生 クラス noone を導出するということにな る ) 。この作業を , メッセージ式て、行うとす るならば , クラス SomeOne に , クラスを追 加するというメッセージを送ることになる のて、ある。実際に Smalltalk ー 80 て、は , その はい 並べられているか その要素は順番に Collection クラスの要素 Dictionary SmaIIInteger B yteArra y 84 CMAGAZINE 1991 1 はい IdentityDictionary キーで参照できるか いいえ 重複を許すか はい Bag いいえ S et はい ArrayedCoIIection Text Character クラスの要素 SequenceableCoIIection 順序付けの方法 外部 LinkedList Link クラスの要素 いいえ キーで参照できるか Array 内部 OrderedCoIIection 任意 String 任意 RunArray