S 日 0 プログラミング Fig. 4 メニュー画面 Fig. 5 窓口会計処理画面 簡易卩 0 S 窓口会社処理 処理を選択して下さい カド名価 人数 タの追加 商品デ 2. 商品データの修正 3. 窓口会計処理 燼 S を入力したい場合には , 商品名の入力時に List 4 は商品コードから商品名 , 単価など れている。しかしこのディスクは 5 インチ 2 ソ〃を入力する。すると個数入力のルーチン の情報を獲得する関数てある。本プログラ HD< あり , かっ MS ー DOS フォーマットもの に入るのて , そこて個数を入力する。ひと ムはもともとマルチューザ用に作成してい なの <,SPARCLT ないし SUN ワークステ つの商品の入力が終了すると , SP300 に商品 るのて , データを探索中は ISAM をロックし ーションて使用するためには , 媒体変換お コードと価格が印字される。商品名の入力 ている。この役割を担っているのが LKISA よびコンバートが必要てある。まず , 3.5 イ 時に一キーを空打ちすると終了とな ンチ 2DD フロッピーを MS ー DOS 上てソ 9 クつ M 関数てある。 り , SP300 に総合計が印字される。窓口会計 List 5 は , 外部変数を初期化する関数。 fld き <FORMAT する。次に MS-DOS 上てこ 処理自体を終了したい場合には , 商品名入 。 rd はフィールドの順序を指定する変数て のフロッピーにファイルをコピーする。最 カ時 * 〃を入力すればよい 後にワークステーション側 <MS-DOS/SU ある。 fldtyp は , 文字型 , 数値型などのフィー ルドのタイプを指定する変数てある ofldlen は NOS のコンバータを起動し , このフロッピ 以上の処理を行うには , あらかじめデー 各フィールドの長さを指定する変数てある。 ーから同ファイルを読み込めばよい。 SPA タベースに各商品に関するデータを入力し RCLT の場合には , 以下のような手続きて ておく必要がある。メインメニューにおい 付録ティスクの 行う。 て商品データの追加を選択すると , 商品デ 使用について % /bin/su ータ入力画面になるのて , 商品コード , 商 Password : ( パスワードを入力 ) 本プログラムは , 付録ディスクに収録さ 品名 , 売価などをそれぞれ入力する。 Fig. 6 markndb. P「2 プログラム 次のソースプログラムを参照してほしい List 1 はデータベースの定義に関する定数を 定義しているヘッダファイルてある。 List 2 は外部変数を定義しているヘッダファイル てある。 List 3 は main 関数てある。 markndb. pr2 (Fig. 6 ) は , データベースを定義しているパ ラメータファイルてある。画面表示に関し ては , curses ライプラリを使用している。ま た OPENISAM など大文字だけの関数は , c ー tree の関数てある。 utextke2 は , 前回紹介 した罫線表示関数てある。 12 6 4 3 0 marcust. dat 144 3 marcust. idx 8 0 8 0 4 20 114 16 0 1 marhin. dat 80 5 marhin. idx 10 0 10 0 2 marden. dat 50 6 marden. idx 4 0 4 3 7 16 8 0 8 28 10 0 1 2 8192 0 0 1 1 8192 0 8192 1 3 0 0 0 1 8192 1 1 32 1 0 32 1 0 1 0 8192 1 1 32 1 0 2 8192 1 1 0 1 0 32 1 0 32 1 0 1 1 12 14 SPARC LT プログラミング 107
List 3 : 4 : 乱数クラス 1 : class Random { 2 : public: void lnit() { random i ze ( ) int Get (i nt n) { return random (n) List int Get() { return random ( 6 ) 十 1 : } void lnit() { randomize()} } 2 : public: 1 : c lass D i ce { サイコロクラス 4 : 3 : スタンスには記号的な意味合いしかありま せん。 こういったクラスはオーバヘッド て、は , を生み出すばかりて無意味なのかというと , それは違います。たとえば , さらに用途を 限定してくサイコロクラス > にしてしまう とわかりやすいかもしれません (List 7 ) 。 Dice : : Get の働きが 1 ~ 6 の値を返すよう に限定されているだけて , ほかは Random と 同じてす 0GetDiceVaIue() などという名前 の普通の関数を用意したほうが , 多少は効 率がよいかもしれません。しかし , そうい う漠然とした , 空中から値を引き出してく るような関数よりも , Dice クラスのインス タンスという実体から値を引き出してくる イメージのほうがはるかにすんなりと使え る気がしないてしようか。たとえ , 実質的 な意味はないとしても。 対象がくサイコロ > という即物的なもの だから特別に感じるということもあります が , これは FindFiIe といったほかのクラス すべてに共通していえることてす。普通の トップレベルの関数を利用するのとクラス のメンバ関数を利用するのとては感覚が異 なります。「インスタンスへの感情移入」と ーム てもいうべき現象が起こるのてす。 ムを書いてみます。正式な名称は知りませ ません。そこて Dice を利用して簡単なゲー 理屈ばかり述べたてていても仕方があり ( 付録ディスク収録 ) のようになるかもしれ ません。ユーザの賭け手を getchar( ) て、受 け取り , 変数 userHand に保持します。一方 てコンピュータ側は , Dice のインスタンス aDice および bDice から賽の目を求め , 丁半 を computerHand に保持します。このふたっ の変数を比較して勝負を決定すれば終わり て、す。しかし , それだけて、はおもしろくあ りません。何回かの勝負を通してのトータ ルて、の点数争いも加えます。そのために ューザ / コンピュータ双方に勝ち回数カウン タを用意します。 このソースを眺めてまず気づくのは , な んだか各種の変数が入り乱れていて見苦し ひとつには , い印象があることてす。 userWins userHand computerWins computerHand という対照関係があるにもかかわらず , れらの変数がバラバラになっている点があ げられます。構造体にまとめるという方法 もありますが , いっそのこと List 8 のような クラスを作ってはどうてしようか。このク ラスのインスタンスとして User と Computer があるようにすれば , 対照関係はより明確 になり , ゴチャゴチャした感じも軽減され るてしよう。とはいうものの Status : : hand List CHOUHAN hand; / / 手 2 : public: 1 : c lass Status { ・勝ちチェッククラス の値を決定するやり方は , ユーザとコンピ ュータとて異なります。ューザは丁半を直 接的に選択し , コンピュータは乱数によっ て間接的に決めます。これは , このゲーム における「ユーザ」というもの「コンピュータ」 というもの , それぞれの性質と考えられま す。となると ,User と Computer とを同じク ラスに属させて事足れりとするのは疑問て す。 て、は List 9 のようにクラスを別々にしてお のおののメンバ関数 Get ( ) が , それそ・れの やり方て hand の値を決めるようにすればよ いのてしようか ? とりあえず , その線に添って書き直して みたのが BON2. CPP ( 付録ディスク収録 ) て す。このソースをじっと眺めるといくつか よく似たクラス 気づかされることがあります , ません。 には同じクラスだといっても過言てはあり 似たものどうしてす。というより , 実質的 か , 細かい違いはあるけれども両者は実に か , Computer はサイコロを持っていると ンバ関数 Get ( ) のインプリメントが違うと いによく似たクラスてあるといえます。メ るをえないとはいっても , それにしても互 User と Computer は別個のクラスにならざ んが , いわゆる盆ク う例のゲームてす。 丁か半か , とい 3 : 4 : 5 : 6 : / / 勝ち数カウンタ i nt W i ns : int Win() { return 十十 wins; } void lnit() { wins ューザ対コンピュータという 1 プレーヤー 型のゲームにします。素直に書けは BON. CPP 130 C MAGAZINE 1 1 9
工ル・エス・アイジャパン lnformation from Compiler Makers し引 C -86 Ver. 3.30 Q コンノヾイラが•can't open 'XXX'" と表示して止まってしまい ます。 A 環境設定が誤っていないか , 確認してください 1. CONFIG. SYS に FILES = 20 ( あ るいは 20 以上 ) と設定しています 2. ヾ XXX ″がヘッダファイルのと きは , lcc に正しく一 I オプション を与えていますか ? 3. •XXX" がソースファイルのと きは , ファイル名を間違えてい ませんか ? Q scanf ( ) で doub 厄型の変数 に % f で値を代入したいのですが , 正しい値をセットしてくれません。 A % f は float 型変数への代入て す。 double の変数への代入は % If としてくださいいドは小文字の L てす ) 。 long double 型への代入に は % Lf を使います。 prinf( ) の場合は , float 型の値は d 。 uble に拡張して渡されるのて % f も %lf も同じ動作をします。これ に対して anf ( ) はポインタを渡し ているのて , 1 , L によって大きさ を正しく指定しなければなりませ て , A す。 0 ry Q ん。 フーが発生したのかを調べてくだ どのプログラムの実行中に工 まず lcc に一 v オプションを与え ″と表示して止まってしまいま コン / ヾイラが•out Of mem 152 C MAGAZINE 1 的 1 9 ・ cpp のとき マクロ定義の数が多すぎます。 不要なマクロを削除してくださ ・ cg86 のとき 工ラーが出た関数が大きすぎて あっかえません。関数を分割し てください ・上記以外のとき ファイルが大きすぎてあっかえ ません。ファイルを分割してく ださい どの程度の大きさまてならコン パイルて、きるのかを定量的に示す ことは困難て、す。なぜなら , コン パイラの内部状態はソースプログ ラムに大きく依存するからて、す。 Q char buf [ 5000 ] ; というロ ーカル変数を宣言すると , 作成し た実行ファイルが暴走してしまい ます。 A デフォルトのスタックサイズ は S, P モデルては 2500 バイト , D, L モデルては 5000 バイトてす。 大きなローカル変数を使用すると きには , lcc の一 k オプションを使っ て , リンカにスタックサイズを与 える必要があります。 Q 他のコンパイラでは通ってい たプログラムを L 引 C でコンパイル すると Fig. 1 のような Warning がた くさん出てしまいます。 A 宣言なしにいきなり使用され た関数は , int xxx( ) ・ という宣言があったとみなされま すが , 実際の関数定義がこれと異 なっている場合には正しく関数を Warning List 1 1 : int func() function 'XXX' undefined assumed tO be int 3 : 4 : 5 : 6 : 7 : 8 : double x = 3.14159 : = 3.14159 ) if (x return 1 ; else return 0 : 呼び出せないことがあります。 LSI C は , このような単純なミス を防ぐためにチェックをきびしく し , プロトタイプ宣言の使用を強 く勧めています。したがって多く の Warning が出るのて、すが , これ を出さなくするためには使用する 関数のプロトタイプ宣言を作成す るか , lcc に一 wi オプションを使用し てください List 1 の関数が 0 を返してしま います。 LSI C は浮動小数点定数はす べて long double 型て、あっかいま す。したがって if 文の条件式は d 。 u ble 型の x と long double 型の定数 との比較になり , x が long double に拡張されてから比較されます。 3.14159 は内部表現ては無限小数 てす。これを x への代入て double に 変換すると , 下位のビットが丸め られます。さらにこの double の値 を long doubléに変換すると下位ビ ットには 0 が追加されますから , も ともとの long double の値とは異な る値になってしまいます。 これを避けるためには , x を long double 型にするか , 3.14159 を dou Q A ble 型の変数に代入し , double 型の 変数どうしを比較してください。 Q リンクするファイルの数が多 くなって , コマンドラインの 1 行 ( 128 バイト ) におさまらなくなって しまいました。 A このような場合には lcc やⅡ d の 第@ クオプションを利用してくださ い。ヾ @ 〃のうしろにファイル名を 書くと , コマンドをそのファイル から読み込みます。このファイル を「応答ファイル」と呼びます。 lcc -ms @res -lmylib この例ては , lcc はまずー ms オプシ ョンを解釈します。次に @res によ って , res という名前の応答ファイ ルを読み込み , コマンドとして解 釈します。そして最後に一 lmylib を 解釈します。したがって , ソース ファイルの名前をファイル res の中 に書き込んておけば , ソースファ イルの数がどんなに増えてもだい じようぶてす。 make を使用する場合はもっと簡 単てす。 make の ${ . . } を使え ば , 応答ファイルを自動的に作っ てくれます。
実力養成講座 5 List 3 のようにして , private 部 , public 部 という形てメンバ ( 変数 / 関数 ) へのアクセス 権を外部に公開するかどうかの制御がて、き ます。インタフェイス部 , インプリメンテ ーション部に近い概念て、す。 private: などはいちおう , 文法的には予 約ラベルに分類されますが , default : ラベ ルなどとは違って , ひとつのクラスの中に private ラベルが複数あってもかまいませ public : private . public : struct { ん。つまり , ないクラス ( すべてのメンバを外部に公開す パプリックメンバしか持た のひとつ覚え <class を用いるべきてしょ とはいうものの原則的にクラスにはバカ いてしよう。 CI ass struct だといい切っても問題はな は struct< 宣言したって別にかまいません。 としておいて , いざ内容を記述するときに class FindFiIe , とりあえず予約しておく場合がありますが , 前方参照のために , クラスの名前だけを ぎません。 になっているクラスてある」という違いにす クラスてあり , cla とはデフォルト <private はメンバがデフォルトて、 public になっている 両者の違いは非常に希薄てす。「 struct と ます。 かに class というキーワードも用意されてい 構造体として扱ってきましたが ,struct のほ こまては , クラスを単なる拡張された キーワード c ass よってしばられることはありません。 かまわないのて、 , メンバの並び順がそれに のように入れ替わり立ち替わり出てきても るクラス ) を class { public : のように書くのは , バカバカしく思えます。 struct の 1 語て、済ませることがてきるのてす から。しかし , いくら構造体の仕様が拡張 されたとはいっても , 従来のような単なる 変数のフォルダーとしての構造体の存在意 義が薄れたわけて、はありません。そういう 単純な構造体とクラスとしての構造体とを ごちゃまぜにするのは , 混乱を招くおそれ があります。 ー・、。 St ok クラス クラスにはもうひとつ「使い心地」という 側面があります 0FSTRTOK. H ( 付録ディス ク収録 ) と FSTRTOK ℃ PP ( 付録ディスク収 録 ) を見てください。標準ライプラリ関数 strtok ( ) は , 静的なバッフアを使っていま す。したがって , 同時に複数の文字列を扱 うことはて、きません。つまり , 複数の文字 列から平行してトークンを切り出すような 処理には利用て、きません。ましてや再帰的 に使うなどもってのほかて、す。 その制限をとつばらおうと試みたのがこ の StrTok クラスて、す。たとえば , List 4 の ように , 切り出したトークンをさらに別の 区切り子を用いて切り分けるといった , 入 れ子状の使い方もてきます。原理的には情 報を静的変数に保存するのてはなく構造体 を介して動的に受けわたすようにすればよ いだけの話てす。それこそ findfirst ファミリ にならって , List 5 のような strtok ファミリ スタートアップ C 十十 を築いても同じはずて、す。しかし , 不思議 なことにこれてはちっとも使いやすそうに は見えません。「構造体を使う関数を使う」 のと「クラスのメンバ関数を使う」のとて、は , 実質的な違いはないはずなのに , 「使う」が 1 個少ないぶんだけ抵抗感なく使うことがて きるようて、す。 D e クラス FindFiIe も StrTok も , クラスというもの を「構造体とそれを利用する関数群」をオプ ラートにくるんて、飲み込みやすくする目的 て、利用しています。しかし , 「構造体」が単 体て、存在し得るのと同じように「関数群」だ けをまとめるクラスというものもあり得ま す。たとえば , srand ( ) や rand ( ) をまとめ たく乱数クラス > というのはどうて、しよう。 List 6 の Random にはメンノヾ変数は存在せ ず , 従来の構造体としての性質は完全に抜 け落ちています。仮に Random が , lnit によ って初期化された乱数系列 ( 「乱数表」といっ てもよい ) をメンバ変数として保持するよう なインプリメントになっていれば , 「 Random のインスタンスを複数使って複数の乱数表 を使い分けることがて、きる」といえます。し かし , 相手はなにしろ乱数てす。そんなこ とをしてもメリットがあるか疑問てす。 のようにグローバルな乱数表を利用するイ ンプリメントて十分てしよう。となると , この Random のインスタンスにはどのよう な意味があるのてしよう ? としか答えようがありません。 ない ういったクラスの場合には , 特定の用途に 供する関数群をグループ化していることだ けが意味を持ち , そのクラスの実際のイン List 2 : 3 : 5 : 6 : 7 : char *last; char *tail; 1 : struct stblk { s ok ファミリ int strtok-next(struct stblk *blk) ; int strtok_first(char *s, struct stblk *blk, const char *dlm) : スタートアップ C 十十 129
Fig. 2 インライン関数として定義 Fig. 1 メンバとする関数の定義 class クラス名 { 〃メンバ関数の定義 関数の型関数名 ( 引数並び ) 関数本体 〃メンバ関数のプロトタイプ宣言 関数の型関数名 ( 引数並び ) : / / プロトタイプ宣言された関数定義 関数の型クラス名 : : 関数名 ( 引数並び ) 関数本体 class クラス名 { 〃メンバ関数のプロトタイプ宣言 関数の型関数名 ( 引数並び ) : / / プロトタイプ宣言された関数のインライン定義 inline 関数の型クラス名 : : 関数名 ( 引数並び ) 関数本体 に「クラス名 : : 」を記述し , どのクラスの イベートメンバとなり , キーワード public を メンバとなる関数かを指示する。クラスの ラベルとして記述したあとのメンバは , パ メンバとなる関数をメンバ関数と呼ぶ。 プリックメンバとなる。 クラス定義内てメンバ関数が定義された class クラス名 { 場合に , そのメンバ関数はインライン関数 / / プライベートメンバ として扱われる。また , クラス定義とは別 メンバ変数 にメンバ関数が定義された場合は , 関数呼 メンバ関数 び出しとなる。ただし , クラス定義とは別 public . に定義されたメンバ関数をインライン関数 メンバ変数 として定義する場合は , メンバ関数定義の メンバ関数 際に , キーワード in ⅱ ne を関数の型の前に記 述する (Fig. 2 ) 。メンバ関数に対し , データ また , クラス定義時にラベル private : を としてのメンバをメンバ変数と呼び , すべ 己述することて , パプリックメンバ以降に てのメンバ変数とメンバ関数をまとめて呼 プライベートメンバを指定することも可能 ぶ場合は , メンバと呼ぶ。 て、ある。ラベル private : や public : は , 複 もうひとつの拡張された概念て、ある , メ 数混在することが許されている。 ンバのスコープについて解説する。構造体 クラス名 { class のメンバは , 構造体変数が外部変数て、ある / / パプリックメンバ public : ときや関数の引数として指定されると , ど メンバ変数 の関数 ( ューザ関数 ) からもメンバを参照 ( ア メンバ関数 クセス ) てきるが , クラスてはユーザ定義関 / / プライベ ートメンバ 数から参照てきるメンバと , そうて、ないメ メンバ変数 ンバをクラス定義時に指定てきる。 メンバ関数 他の関数から参照てきるメンバをバブリッ public : クメンバ ( 変数 , 関数 ) , メンバ関数とフレ ンド関数以外の関数から参照てきないメン バをプライベートメンバ ( 変数 , 関数 ) と呼 ぶ。これらは , クラス定義時に区別され , キーワード class 以降にあるメンバはラベル が別の保護レベルを示すまて自動的にプラ しは , 構造体て、のメンバの参照と同様て、あ る。クラス変数を宣言することにより , そ の実体が記憶領域上に割り当てられ , 実体 に対しては演算子 ! ク ( ピリオド ) を使い クラス変数名 . メンバ変数名 て、値の参照を行う。 メンバ関数の呼び出しは , クラス変数名 . メンバ関数名 ( 引数 ) て、行われる。実体を指すポインタの際には , 演算子、、一 > 〃を用いる。パプリックメンバに 対しプライベートメンバは , ほかの関数か らは参照が行えない。したがって , プライ Fig. 3 メンバの参照 class クラス名 { メンバ変数 p 「 a 関数の型メンバ関数 p 「 af ( ) / / バブリックメンバ 1 三ロ public : メンバ変数 pub , 関数の型メンバ関数 pubf ( ) メンバ変数 p 「 a 十十 : メンバ関数 p 「 af ( ) : private . main( ) クラス名クラス変数 1 : クラス変数 1 . メンバ変数 pub = 値 , クラス変数 1 メンバ関数 pubf ( ) : クラス名 * クラス変数 2 , クラス変数 2 = & クラス変数 1 : クラス変数 2- > メンバ変数 pub = 値 : クラス変数 2 ー > メンバ関数 pubf ( ) : / / バブリックメンバ メンバの参照 ューザ関数からのパプリックメンバとな るメンバ変数の参照やメンバ関数の呼び出 124 C MAGAZINE 1 1 9
ンバ変数とし , スタックを操作する Push , P 叩の操作をパプリックなメンバ関数として クラスを定義する ( Fig. 5 ) 。スタック上のデ ータはプログラムを構成するユーザ関数か らの直接操作は遮断される。したがって , データは保護される。また , プログラムて はデータを Push するときはメンバ関数 Push を , P 叩する場合はメンバ関数 P 叩を呼び出 すだけて , スタックを操作てきるわけてあ る。モジュールからの直接的なデータへの アクセスがなくなり , Push, P 叩以外の操 作はユーザ関数からまったく行えなくなる。 データの保護が実現されスタックという抽 象的な対象をモデル化・部品化しているわ けてある。このように , クラスはデータの 保護とデータの抽象化を実現する手段とな ラスの初期化と消去 クラス変数を宣言したり , メモリ管理演 算子 new を使用することによりクラスの領域 が記憶域上に確保される。 C プログラムの初 心者は , 変数は宣言したものの変数に値を 代入せずに演算したり , 変数がポインタ変 数てあるにもかかわらず , ポインタを代入 しないままポインタ処理を行うなどの初歩 List コンストラクタとディストラクタ class Char int publ ic: len; 的なミスを犯しがちて、ある。 クラスにおいてもクラス変数を宣言した り ,new により領域を確保した後てメンバ変 数に値を代入しないまま何らかの操作を行 う可能性はある。まして , メンバ変数は通 常の変数のような形式ての初期化は行えな い。クラスには , 変数の宣言や new によるク ラスの領域が確保された時点て , 自動的に 初期化を行うためのメンバ関数を設定てき る。このメンバ関数をコンストラクタと呼 ぶ。 メンバ変数がポインタ変数の場合 , コン ストラクタ内て演算子 new により領域を確保 し , そのポインタを持つようなポインタ変 数てあれば , クラス変数のスコープ ( 有効範 囲 ) が終了する際に , 確保された領域を解放 してスコープを終了する必要がある。もし , 解放を行わないとすると new によって確保さ Fig. 7 引数を持つようなコンストラクタ class クラス名 { main( ) Fig. 6 コンストラクタとディストラクタ class クラス名 { メンバ変数 public . / / コンストラクタの定義 クラス名 ( 引数の並び ) 〃コンストラクタのプロトタイプ宣言 クラス名 ( 引数の並び ) : / / ティストラクタの定義 ークラス名 ( 引数の並び ) ″ティストラクタのプロトタイプ宣言 ークラス名 ( 引数の並び ) : / / ほかのメンバ関数 ″コンストラクタの定義 ラス名 : : ークラス名 ( 引数の並び ) ケイ . トラクをの定義 : ークフス名 ( 引数の並び ) クラス名変数名 ( 引数の並び ) : れた領域は , 管理されずプログラムの終了 まて存在することとなりメモリ領域のムダ となる。このようなケースに対し自動的に クラスの後始末 ( 消去 ) を行うメンバ関数を 設定することもてきる。このメンバ関数を ディストラクタと呼ぶ。 コンストラクタやディストラクタを設定 すれば自動的にすべてを管理するわけては ない。それぞれのメンバ関数内て初期化や 後始末の手続きを定義する必要がある。コ ンストラクタ設定時のメンバ関数名は , ク ラス名と同じてあり , ディストラクタのメ ンバ関数名は一クラス名 ' ' ( チルダ ) とす る (Fig. 6 ) 。 コンストラクタやディストラクタは多重 定義 ( オーバロード ) が可能てあり , 引数の デフォルト値も設定てきる。コンストラク タの呼び出しは , クラス変数の領域が記憶 域上に割り当てられた時点て自動的に呼び 出される。そして , 引数を持つようなコン ストラクタては , Fig. 7 の形式て引数を指定 する。 ディストラクタの呼び出しは , クラスの 領域が変数の宣言によって確保されていれ ば , その変数の有効範囲が終了する時点に 演算子 new によって確保された領域てあれば delete によって解放される時点に自動的に呼 び出される。前述したようにディストラク タはクラスの後始末を行うためのもてある。 List 1 のようにコンストラクタによってクラ ス内部て領域を確保しているような場合に は , ディストラクタによって解放を行う。 List 1 ては , 〃 1 と / / 2 の処理において自動 的にコンストラクタが呼び出されている。 コンストラクタ内て記憶域を確保し , プラ X( char* p) len =•trlen( p ) : new char[len 十 1 ] : S delete CIen 十 l]s; strcpy( s, p) : 126 C MAGAZINE 1 1 9 delete bp: new *bp X X a( ” ABCD") : main() / / 1 / / 2 / / 3 / / 4 クラス名 * 変数名 = new クラス名 ( 引数の並び ) :
実力養成講座 5 イベートなメンバ変数 s によって領域を管理 している。 〃 3 て , 〃 2 によって確保されたクラスの領 域を解放する。解放される直前にディスト ラクタが自動的に呼び出され , プライベー トなメンバ変数 s が管理している領域の解放 を行う。ディストラクタが終了した後て , クラスの領域が解放されることになる。 〃 4 ては , クラス変数 a の有効範囲は終了 こても , し , 変数の領域が解放される。 『 ! された構造体ーークラス いやしくも「スタートアップ C 十十』なる ものの読者ともあろう方は , クラスとはな んぞや , という記事を以前にも読んだことが あるかもしれません。いわく , ユーザ定義 のデータ型てある。いわく , 個々のインス タンスの性質や属性を抽象した概念集合て ある。うんぬん。 そういうご託宣はとりあえず棚上げして , ありていにいってしまえば , C 十十のクラス とは構造体のことてす。 構造体の仕様は拡張されて , 変数だけて なく関数もフィールドに含めることがてき るようになりました。データもコードもま とめあげることのてきる構造体 , それがク ラスと呼ばれる代物てす。合わせて用語も 変更して , フィールドはメンバといい直さ れ , クラス内変数のことをメンバ変数と呼 び , 関数のことをメンバ関数と呼びならわ します。また , このクラス型の変数は ( クラ ス ) オプジェクトまたはインスタンスと呼び ます。 もっとも単純なクラスの例を List 1 に示し ます。用語を整理して List 1 を解説すると , 「メンバ変数 value と , メンバ関数 lnit, Print とを持つクラス A が宣言されており , a はク クラス変数 a の有効範囲の終了直前一 トラクタが呼び出されることになる。 こディス メンバ関数なのか明示しなくてはなりませ 解決演算子 . : を使って , どのクラスの メンバ関数を定義するときは , スコープ ということになります。 ラス A のインスタンス ( オプジェクト ) てある」 実践編 ールの部品化を容易に実現し , 再利用性を 作をひとまとめにすることにより , モジュ 利点としては , データとデータに対する操 抽象化によりデータを保護する。クラスの クラスは , データの抽象化を実現しその ん。 はすてに説明済みの項目てすが , 以前解説したときは , といった形てした。今にして思えば , これ は ( 空 ) : : a て「トップレベルの a 」を表す という特殊な用法 , ということがわかりま す。 メンバ関数は , 普通の関数と同じように オーバロードもてきるし , デフォルト引数 void Print(FlLE * fp void Print( ) ; struct A { ロードして , も使えます。たとえば A : stdin) : : Print をオーバ メンバ関数をインライン関数にすること などというものを設けることもてきます。 fprintf (fp, "%d*n", value) : void A : : Print(FILE * fp) スタートアップ C 十十 高められるということがあげられる。 C 十十プログラミングては , このクラスを どのように用いるかというプログラム設計 段階のクラス設計が重要になるてあろう。 クラスをどのように用いるかは , 本連載後 半て述べる予定てある。 次回はこのクラスの応用的な機能てある フレンド関数 , 演算子の多義化 , 派生クラ スといった機能を解説する。 白倉伸一郎・山本浩文 もてきます。このとき , キーワード inline を つける必要はありません。クラス A のメンバ 関数をインラインにした List 2 を見てもおわ かりのとおり , inline がなくても誰がどう見 たってインライン関数ばく書けるからてす ( もちろんつけてもかまわない ) 。 それはともかく , なんとも味気ないサン プルてす。用語や書式はたしかにカッコい いのてすが , 実際に行っている処理内容は 大したものてはありません。 A : : lnit ( ) が List 単純なクラスの例 pri ntf ("XdYn", value) : 14 : void A::Print() value 9 : void A::Init(int n) void Print(); vo i d i t ( i nt れ ) : int value; 3 : struct A { 1 : #include く stdiO. h> 24 : 23 : 22 : 8 : 6 : 5 : 4 : 2 : return 0 : a. Print(); a.Init(2); 19 : main() スタートアップ C 十十 127
特集フレタ系言語研究 す。これは「参照された結果がスカラて、ある」 ことを表しています。第三要素といってし まえば ( その値がもともとどこから来たもの かを忘れたとすれば ) , それ自体はスカラだ からて、す。ということは , 結果が配列にな るような参照もて、きるのて、しようか ? そ れもて、きます (Fig. 7 ー② ) 。最後の例を $array [ 3 ] と比べてください。 $ と @ の違いが , 結果がスカラて、あるか配列て、あるかを表し ています。 配列の添字の始まりは特殊変数 $ [ に入っ ていますが , 普通は 0 て、す。これは変更しな いほうがよいて、しよう。なお , perl には配列 の配列 , つまり多次元配列はありません ( 連 @monthname = ( ' Jan' , ' Feb' , Mar' , ' Apr' , ' May' , ' Jun' , 調べる場合に Fig. 7 ー④のように記述て、きて 変数が空つばて、あるかどうかを if や while て、 素数を返します。これを使うと , ある配列 はスカラコンテキスト ( 後述 ) て、は配列の要 り ) 要素数より 1 小さい値を返します。 @array ③ ) 。 $#array は普通 ($ [ を変更しないかぎ れにはふたつの方法があります ( Fig. 7 ー はその時々に調べなければなりません。そ たがって , 配列の大きさを知りたい場合に たびに自動的に大きくなってくれます。し の必要はありません。要素への代入がある perl の配列は C などと違って大きさの宣言 れてはいますが・・・ 想配列を使ったエミュレーションが用意さ ② @array [ 1 , 2 , 3 ] 配列 @array の第一 ~ 三要素を並べてできる新しい配列 配列変数 @array の第三要素 ① $array [ 3 ] Fig. 7 配列変数の要素をスカラ変数として参照 @array [ 3 ] ③ $#array @array 配列 @a 「「 ay の第三要素だけを要素とする長さ 1 の配列 配列変数 @array の最後の添字 配列変数 @array の要素の数 ( スカラコンテキストで ) ④ if (@array) { . . } # データがあれば { } 内を実行 ⑤ ($first, $second, @rest) Fig. 8 pe 日の配列コンテキスト ① @fruits ニ ('apple', 'banana', 'lemon') : @array [ 0 , 3..5 ] ③ 1 .. 3 ($first fruit) ニ @fruits , ② $num fruit : @fruits , ( 1 , 2 , 3 ) # $num fruits には 3 がは入る # $first fruit には ' app ばか入る ($array [ 0 ] , $array [ 3 ] , $ar 「 ay [ 4 ] , $array [ 5 ] ) date もどき List 1 6 便利て、す。 perl て、は配列を式の中て使うことカイきま す。配列の一括代入がて、きたり , 配列を関 数値として返す関数があったりします。 のような使い方をされる一時的な配列をリ ストといいます。 perl て、は式の中て、いきなりカッコを開いて スカラ式をコンマて、区切るとリストになり ます。次のようなものはリストて、す。 ( 2 , 3. 5 , 7 ) ($drive, @path, $base, $extension) ふたつ目の例て、は配列変数@path は要素全 部がリストの中に開かれてしまいます。 perl のリストはあくまて、一次元て、 , Lisp のよう な二分木て、はありません。リストはその要 素がすべて左辺値 ( 代入可能な式 ) てあると き代入て、きます (Fig. 7 ー⑤ ) 。このとき $first には 'ant' が , $second には 'bat' が代入され , 残りの三つは三要素の配列となって @rest に 代入されます。左辺のリスト内に配列がな い場合 , あまった要素は捨てられます。 List 16 は現在の日付と時刻を出力するも のて、す。 localtime は現在の時刻を月 , 日 , 時 , 分などに分解して , リストとして返し てくれる組み込み関数て、す。 printf は C の printf( ) と同様の関数て、す。 月の名前を得るために配列 @monthname を引いています。曜日はちょっと手抜きを して , 配列 ( リスト ) をその場て、作ってその 場て、参照しています。このようにリストと 配列はまったく同等なのて、す。 特殊変数@ARGV には perl が起動された ときのコマンド行引数が入っています。 perl にはリストを操作する関数が多数用意され ています。以後 , 例中て、使う関数以外の詳 1 : 2 : 3 : 4 : 5 : 6 : 7 : 8 : ($sec, $min, $hour, $mday, $mon, $year, $ 響 Y ) = 18a1ti2 : $mon ニ $monthnameC$mon] ; printf("Xs Xs X2d % 02d : % 02d : 加 2d % 02d \ n " , $wday, $mon, $mday, $hour, $min, $sec, $year 〉 = 70 ? 19 : 20 , $year) : 細は , マニュアルを参照してください 己列コンテキスト perl には式の値が配列を要求しているか 特集フィルタ系言語研究 , ス 55
より優先しますいポインタて、あるクが優先 法は分かりました。その逆はどうて、しよう れに対応する前置方向の記号と対を成して します ) 。そこて、読み方は「 bin は , TOOL D いなければなりません。優先順からすると , 次のようなものをコードとして書きたい EF を返す関数を指すポインターーすなわち 後置記号の [ ] が , さらに後置記号の ( ) と としましよう : 「配列のサイズが MAX FU 関数ポインターーて、ある」て、す。 対を成すことになり , ヒたがって , これは 不法て、す。 typedef TOOL DEF ( * BIN DEF) ( ) ; NC PTRS の , 関数ポインタの配列を宣言し おなじく , TOOL DEFbin()C3] も不法 たい。各関数の帰値は文字列だ」。この文 BIN DEF bin ; て、す。本当に書こうとしたのは , TOOL D この宣言の読み方は , 「 bin は BIN DEF 型 を ,TabIe 1 の文を使った文章に書き直すこ EF (*bin [ 3 ] ) ( ) , つまり「 bin は TOOL て、ある。そして BIN DEF は , TOOL DEF とからスタートしましよう。「宣一 て、止めるのて、はなく , 「 bin は」から始めます : を返す関数を指すポインターー関数のポイ DEF を返す関数の一一関数を指す一一ポイ ンタ三つから成る配列て、ある」てす。 *binC3] ンターーて、ある」となります。 「 bin は MAX FUNC PTRS 個の関数ポイン 〔訳注 : 関数をコールすることは , 関数の を囲むカッコが , 記号の優先順を強制して タの配列て、あり , 各関数の帰値は文字列 ( = アドレス ( 関数のポインタ ) をコールするこ 後置記号と前置記号を正しくマッチさせる char'* インタ ) て、ある」。 となのて、 , ある文脈て、は「 BIN DEF は TOO ためには必要て、す。 見慣れた形になってきたて、しよう ? 次 L DEF を返す関数型て、ある」とも言えま typedef TOOL DEF ( * BIN DEF [ 3 ] ) ( ) ; は , 文を記号に置き換えていきます : char * ( * binCMAX FUNC PTRS]) ( ) ; BIN DEF bin ; TOOL DEF * bin [ 3 ] ( ) ; この宣言の読み方は , 「 bin は BIN DEF 型 これを typedef 宣言に書き換えるとした 「 bin は」からスタートし , [ ] が * より優 て、ある。そして BIN DEF は , TOOL DEF ら : 先することに着目します。すると , 「 bin は成 を返す関数を指す三つのポインタの配列て、 typedef char* (BIN DEF CMAX FIJ NC PTRS] ) ( ) ; 分が三つの配列て、ある・・・・・」となります。て、 ある」となります。 TOOL DEF * *bin [ 3 ] ( ) ; は何の配列て、しよう ? 次は , 左側の記号 BIN DEF bin ; を使うのて、しようか , それとも右て、しよう 上の宣言が不法て、あることが , 見てすぐ 変数名へのアクセス に分かりますか ? TOOL DEF * (*bin [ 3 ] ) (int) ; 変数名からスタートしたことに注目して ください (TabIe 1 の、、方向ク欄のどちらの これは , 「 bin は 3 成分の配列て、あり , その そろそろー休みして , C の宣言を楽に見れ 方向の場合ても ) 。そしてすて、に , [ 3 ] は使 各成分は関数のポインタて、あり , その関数 るようになったことを , お祝いしてもよい は引数が int< 帰値が TOOL DEF を指すポイ て、しよう。て、も , あまり長く休まないよう ってしまっています。そこて、 , こまて、解 ンタだ」という意味になります。 読した部分を X と置きましよう。すると宣言 に。まだ終わってはいませんから。 〔訳注 : 同じことの別の読み方は , 「 bin はポ 複雑な宣言やキャストや typedef の中の , は , TOOL DEF * X ( ) となります。 ( ) は * より優先するのて、 , ( ) ーーー、、関数て、あるク インタ三つから成る配列て、ある。その各ポ 変数にアクセスする方法を知る必要がある を選びます。すると「 X は TOOL DEF を のて、す。 インタは , int を引数にとり TOOL DEF を指 TOOL DEF *bin [ 3 ] ; 指すポインタを返す関数て、ある」となりま すポインタを返す関数の ( ~ を指す ) ポイン 私たちは宣言解読のエキスパートて、すか タて、ある」となります〕。 す。 X はすて、に決まっていますから , そこて、 , typedef TOOL DEF * 旧 N DEF [ 3 ] ) (int) ; ら , これが「 bin は TOOL DEF を指すポイン 「 bin は成分が三つの配列て、あり , 各成分は T タ三つの配列て、ある」て、あることは分かりま BIN DEF bin ; OOL DEF を指すポインタを返す関数て、ある」 この宣言の読み方は , 「 bin は BIN DEF 型 す。て、は , これらの成分にどうやってアク て、ある。そして BIN DEF は成分が三つの配 となります。これは不法な宣言て、す。関数 セスすればよいのて、しよう ? 列て、あり , 各成分は , 引数が int て、帰値が T のポインタ ( 関数を指すポインタ , 関数ポイ 配列の各成分には , TOOL DEF 型のデー ンタ , 関数のアドレス ) の配列というものは OOL DEF へのポインタてある関数のポイン タ成分を指すポインタが代入されなければ タて、ある」となります。 可能て、すが , 関数の配列というものはあり なりません (List 1 の TOOL DEF と fieldl えません ! def の宣言を見てください ) 。変数 a , b, c を 逆の角度 この宣言の合法性を判断する方法を , 前 次のように宣言すると : にもうーっ述べました。思い出してくださ TOOL DEF a, b, c ; い。有効な宣言は , 後置方向の記号が , そ fieldl def d ; 方 る 、ん 換 五ロ 一三ロ 英 の っ を 言 宣 の C の宣言がわかるコツ 17
ースタートアップ C + クラス ( 1 ) 実力養成講座 第 。前回まで O 言語に対して拡張された機能について解説 してきた。今回より 2 回にわたり , C 十十の機能の中でも っとも特徴的かつ重要な要素である「クラス」について 解説する。クラスも C 言語を拡張した機能のひとっとし 第て捕えることができる。当然 , 」を拡張された機能の中に一、 は , 新しい概念から生まれたものもある。ここでは , C き語の造体と較からクースの 理論編 とつの単位として扱えるということてある。 『 : ク ? スとは また , 参照てきるメンバと , 参照てきない クラスの定義は , 単に型を決定したにす メンバを区別することもてきる。このよう ぎない。構造体て、は , 構造体の定義を行っ にクラスを構造体の概念の拡張とみなすこ たあとに構造体変数や配列を宣言する。同 クラスは , C 言語の構造体 ( 以下構造体 ) と とにより , クラスの使い方は構造体と同様 同様にユーザが定義する型てあり , 構造体 様に , クラスの定義のあとにクラス変数や 概念を拡張した機能を持つ。構造体は種類 に行える。ただし , 拡張部分の取り扱いが 配列の宣言を行う。クラス変数宣言は , 特徴的なものとなる。 定義済みクラス名変数名 の異なった型のデータをひとつの単位とし て扱うことがてきる。クラスとして拡張さ となる。 クラスの定義 れた概念および機能は , データだけてなく , メンバー そのデータヘアクセスを行う関数まてもひ 構造体がユーザ定義の型てあるように クラスもユーザ定義の型てある。つまり , 構造体の概念を拡張したクラスをメンバ コラム というタームを使ってまとめると クラスを使用するためには , クラスの型の C 十十の構造体 ①関数をメンバとすることができる 定義を行う必要がある。構造体ては , キー C + + にも構造体は存在する。ただし , ②スコープによるメンバの区別 ワード struct により型の定義を行い , 型名に クラス型のひとつの種類として存在す 相当するタグ名をユーザ ( プログラマ ) が決 といえる。 るものである。構造体の定義はキーワ 定した。クラスの定義ては , キーワード class 関数をメンバとする場合は , クラス定義 ード class をキーワード st 「 uct に置き換 の中て関数の定義を行うか , プロトタイプ により型の定義を行い , 型名に相当する「ク えたもので , ラベルが別の保護レベル ラス名 ( タグ名 ) 」をユーザが決定する。ク 宣言を行う必要がある。プロトタイプ宣言 を示すまで自動的にバブリックメンバ ラス定義は , を行ったメンバとなる関数は , クラス定義 となる。つまり , 自動的に設定される class クラス名 { とは別に関数を定義する。 Fig. 1 に示すよう 保護レベルが異なる以外は , クラスと に , クラス定義とは別にメンバとする関数 メン / ヾ 同じものである。 を定義する場合は , 関数の型と関数名の間 龍崎昌平 スタートアップ C 十 + 123