クラス - みる会図書館


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

1. 月刊 C MAGAZINE 1992年12月号

なります。 もちろんいくつか制約があります。一例 をあげると , 派生クラスて、あるとはいえ基 本クラスへのポインタとして使われるわけ 基本クラスと派生クラスの関係 List class B { class BM : public B ( class BMW . public BM { static メンノヾ クラスのメンバは , static として宣言する こともできます。 メンバ変数が static であった場合 , そのク ラスのすべてのインスタンスでたったひと つの変数が共有されます。 class A { public . static int cnt ; A ( ) { cnt 十 + ; } static void func ( ) ; BM bm : BMW bmw; / / 工ラー bm = (BM)bmw; / / これは当然 0 K BM* bmp = &bmw; / / これも 0 K B* bp = &bmw ; なのて、 , 派生クラスて、新たに定義されたメ す。インスタンスが派生クラスのものてあ ンバにアクセスすることはて、きません。 っても , 一度基本クラスへのポインタに代 しかしながら , 大枠は基本クラスのもの 入されてしまうと , 派生クラスのインスタ を使いながら , 基本クラスては役不足てあ ンスとして生まれた使命を忘れ果て , いず ったメンバ関数だけを派生クラスて、定義し れも基本クラスの he110 ( ) が呼び出されてし 直し , 異なる動作をさせることがて、きるわ まっています。鳴り物入りて登場した継承 int A : : cnt けてす。 て、はありますが , こんなところてもう限界 基本クラスを念頭において書かれたソー になってしまうのて、しようか。なんともは この例では , クラス A のインスタンスがい スに ( インスタンスの定義部分を除けば ) 変 や。 くつ作られたかが , cnt に記録されることに 更を加えることなく派生クラスからも再利 やはりクラスとクラスの間には , 暗くて なります。 用することがて、きそうて、はありませんか。 深 ~ い谷があるのか・ また , static メンバはインスタンスがひと 仮に C 流の考え方てなんとかするとすれ ~ 単純な継承の限界 つもなくても , メモリ中に存在しています。 ば ,List 3 のような解決法が手つ取り早いて このため , static メンバ変数は A : : cnt とい う書式でもアクセスできます。もちろん , ことがそう簡単に運 確かに , これて当面の目的を達成するこ ところがどっこい 従来どおり , a. cnt という形式も使えます。 んて、しまっては , 黄門様の出番もないとい とはて、きます。しかし , 派生クラスが増え メンバ関数が static であった場合も同じ うもの ( 誉 ; ) 。実際にはちょっとめんどう るたびに基本クラスの enum を増やさなけれ く , a. func(), あるいはインスタンスなし なことが起こってしまいます。 ばならないし , 本来ならまったく存在理由 で A : : func ( ) のどちらの書式でも呼び出 定石どおり , あとて、ばっちり解決といき のないはずの call he110 ( ) などという関数を すことが可能です。 ますから安心して読み進んて、ください ( 笑 ) 。 導入しなければなりません。これじゃ何の インスタンスなしで呼び出せるというこ ために継承を使ったんだか。本末転倒って さて , 派生クラスへのポインタが基本ク とは , static メンバ関数にだけは this ポイン ラスのポインタとしても扱えることはわか やつじゃありませんか。 タが渡されないことを意味しています。し こて、 , List 2 を見てください りました。 仮想関数 たがって , staitic メンバ関数は , static メン このリストて、は , クラス A の派生クラスとし バ変数へはいつでもアクセスできますが , 非 static メンバにアクセスできるわけではあ ンバ関数 hell 。 ( ) を持ち , 基本クラス A への ということて , これをスマートに扱うた りません。 ポインタを通して呼び出しています。 めにあるしかけが用意されています。それ a. func ( ) という形で呼び出されたとして て、私達が期待するのは , ( 1 ) てはクラス A の がお待ちかね ( ? ) 仮想関数てす。 も , インスタンスへのポインタでも引数に he110 ( ) が呼び出され , ②てはクラス AA の Fig. 1 実行結果 とらないかぎり static でないメンバ変数のア he110 ( ) が呼び出されることてしよう。 A: : hello() クセスができないことになります。 しかし , 実行結果 ( Fig. 1 ) を見ると , どう A : : h 0 ( ) も期待したとおりには動いていないようて、 122 C MAGAZINE 1 2 12

2. 月刊 C MAGAZINE 1992年12月号

ウクラスを登録する必要はなく , このよう , リソースて、 AFX IDI STD FRAME を 定義するだけて、済みます。 以上て、 List 28 を説明しました。モードな しダイアログボックスは , メインウインド ウと同時に出現します。数字を入れて AppI y ボタンを押すと , メインウインドウの線の 間隔が変化します。また , メニューから Ab out を選択すれば , モードつきダイアログボ、 ックスが出現します。実行画面のイメージ を Fig. 27 に示しておきます。 ウインドウクラスの登録 アイコン以外のウインドウクラスのパラ メータを , デフォルトから変更するときに は , ウインドウクラスを新たに登録する必 要が生じます。 MFC ライプラリは , しばし ば変更されるパラメータのためのユーティ リティ関数を提供しています。 const char * AfxRegisterWndClass (UINT nClassstyIe, HCURSOR hCursor HBRUSH hbrBackground H ℃ ON hlcon この関数は , クラスのスタイル , カーソ ル , バックグラウンド塗り潰し用プラシ , クローズ時のアイコンの四つを引数て、渡す と , ユニークな名前て、新しいウインドウク ラスを登録し , その名前を返します。 CWnd クラスのメンバ関数 Create の第 1 引 数は , ウインドウクラスの名前を指定する のて、すが , これまての例て、は NULL が渡さ れていました。 NULL て、デフォルトのウィ ンドウクラスが使われるようになっている わけて、す。新しいウインドウクラスを用い るには , この引数に AfxRegisterWndClass の戻り値を渡せばいいのて、す ( List 30 ) 。 め 以上 , MS ー C7 の C 十十の機能と MFC ライ プラリについて概要を説明しました。 MFC ライプラリは Windows API をカプ セル化しているおかげて、 , 非常に規模の大 きなものとなっており , かぎられた誌面て、 は , すべてを紹介することはてきませんて、 したが , 雰囲気はだいたい伝えられたのて、 はないかと思います。 今回まったく触れませんて、したが , MFC には , そのほかに , OLE(Object Linking and Embedding) を支援するクラスもありま す。これについては , また別の機会に紹介 したいと思います。 付録ディスクには , MFC ライプラリを用 いたアプリケーションの例として , 神経衰 弱ゲームが収録してあります。また , 本誌 ' 91 年 6 月号に掲載されたプラネタリウムプ ログラムを原著者の了解を得て再録すると ともに , 比較のために MFC ライプラリを用 こちらもあわ いて書きなおしてみました。 せてご覧ください P A R 怡土昌彦 T Windows ソフトウェア開発キット 以前までの MS - C には , OS / 2 用 ( PM 対応を除く ) プログラム 開発キットがバンドルされていました。しかし MS - C7 では OS / 2 を切り捨て , Windows SDK ( ソフトウェア開発キット ) か バンドルされています。本ノ←トでは SDK に焦点を当てます。 W ⅲ dows ソフトウェア 開発キットとは Windows ソフトウェア開発キット ( 以下 S DK と表記 ) とは , 名前が示すとおり Windo ws 対応のソフトウェアを開発するためのラ 72 C MAGAZINE 1 2 12 イプラリとツール集と思っていただいても 差し支えないて、しよう。 以前の MS-CVer. 6.0 て、は , 開発キット なして、 OS / 2 用のソフトウェアを開発て、きた のに , な€Windows のソフトウェアを開発 するのに , そのようなものが必要になるの か疑問に思う方もいらっしやると思います。 そのような方は , もう一度 OS / 2 の姿を思い 起こしてください。 OS / 2 は , MS-DOS のよ うな側面 ( コマンドプロンプト側 ) と , Wind ows のような側面 ( プレゼンテーションマ不 ージャ ( PM ) ) を併わせ持ていたはずて、 す。そんな OS / 2 のコマンドプロンプトに対 応したプログラムを作成するのに開発キッ

3. 月刊 C MAGAZINE 1992年12月号

リ The 0 ◆ PushCwd クラスと PushCdCwd クラス ラミングを可能にするか , という見極めて す。 市販のクラスライプラリて、はこのもっと もめんどうな部分がすて、に形になっている わけて , プログラマは自分に必要な処理の みをオーバライドしてしまえばよいのてす。 18 : } ; 13 : ( 17 : } List 3 : 4 : 5 : 8 : 10 : 13 : 14 : 15 : 16 : 17 : 1 : class PushCwd { 2 : public: char 1 wd CMA XPATH] ; PushCwd() { getcwd(lwd, virtual void back() chdir(lwd); last working directory sizeof(lwd)) ・ / / ディレクトリを元へ戻す あとは野となれ山となれ。 : -P ツ ラスを指定した メンバ関数呼び出し クラスのメンバは基本クラスのデストラク ンバは派生クラスのデストラクタが , 基本 してだけ責任を持ちます。派生クラスのメ 自分のクラスだけが持っメンバの破壊に対 クタてす。あるクラスのデストラクタは , その代表例は , なんといってもデストラ いうケースは , 実際非常によく出てきます。 もしますが , 基本クラス十の処理て、十分と す。これを差分プログラミングといったり たいだけ , ということも往々にしてありま 換えるときもありますが , ちょっと追加し るとき , 基本クラスの処理をそっくり置き 派生クラスて、仮想関数をオーバライドす タが破壊するわけて、す。 Cwd へ挨拶を怠ってしまえば , 当然基本ク wd のメンバ関数 back( ) が基本クラス Push てしまいます。派生クラスてある PushCdC うと , 連絡不行き届きは自分の責任となっ ら抜け出てメンバ関数として独立してしま しかし , コンパイラの厚い庇護のもとか んてした。 てくれたのて , とくにもめごとも生じませ クラス関係各方面への挨拶もきちんとやっ う場合は , コンパイラに任せておけば基本 デストラクタてディレクトリの復帰を行 ことにします。 バ関数て、明示的にディレクトリを元に戻す デストラクタて、はなく , back ( ) というメン クラスを再掲載します。ただしここて、は , さて , List 7 に前回にも出した PushCwd 要はありません。 出しますから , 我々が明示的に呼び出す必 自動的に基本クラスのデストラクタを呼び 前回ても説明したように , C 十十処理系は 11 : class PushCdCwd : publ ic PushCwd { 12 : public: List void back() setdisk(tolower(lwdC0])-'a'); / / ドライブを元へ戻す PushCwd : :back() ; / / 基本クラスの back ( ) を呼び出す 仮想関数の呼び出し 3 : 4 : 6 : 9 : 11 : 14 : 15 : 16 : 1 : class Base { 2 : public: virtual void fa() { fb(); } virtual void fb(); 7 : class Derived : public Base { 8 : public: void fb(); / / Base クラスの fb ( ) をオーバーライド 12 : void main() Derived derived; / / 呼び出しが起こる 〃 Base::fa() ー > Derived::fb() という derived. fa() ; ラスては「そんなの知らんね」とそっほ。をむ いてしまいます。すべての処理は自分て、や らないといけないのて、しようか ? もちろ んそんなことはなく , 基本クラスの back ( ) を呼んて、しまえばことは丸く収まるわけて、 す。このときの構文が , クラス名 : : メンバ名 てす。直接の基本クラスてなくとも , 曾祖 父のクラスだろうが 10 代前の基本クラスだ ろうが呼び出すことがてきます。それだけ てはなく , 自分自身のクラスに対しても指 定がてきます。 仮想関数を使っていると , 自分のクラス のメンバ関数が確実に呼び出せるとはかぎ りません。 List 8 て , クラス Base の fa() は , 自分のクラスの ( ) を呼んだつもりか もしれませんが , 実際にはクラス Der ⅳ ed の 用 ( ) が呼ばれます。たいていの場合はこれ が期待する動作となるのて、すが , そうて、な い場合もあるかもしれません。コンパイラ にはそんなことわかりつこないのて、 , プロ グラマが指示する必要があります。 このように どうしても自分のクラスの 仮想関数を呼ばなければならない場合には , 確実を期すために Base . と記述する必要があります。 ⅵ「 tu 引なテストラクタ デストラクタは C 十十コンパイラが勝手に 呼び出してくれるのて、すが , そのためには 最低限 , インスタンスがどのクラスのもの なのかを知っている必要があります。 しかし , 派生クラスが基本クラスへのポ インタへと姿を変えたあとには , 「派生クラ C + + 入門講座 125

4. 月刊 C MAGAZINE 1992年12月号

特集日本版几十十 の全貌 Fig. 20 MFC コレクションクラス CObject CByteArray CWordArray CDWordArray CPt 「 Array CObArray CStringArray CAr 「 ay く TYPE> CPtrList CObList CStringList CList く TYPE> CMapWordToPtr CMapPtrToWord CMapPtrToWord CMapStringToPtr CMapStringToOb CMapStringToString CMapWordToOb CMap く KEY, VALUE> Fig. 21 MFC 例外クラス CObject L CException CMemoryException CNotSupportedException CArchiveException CFileException CResourceException ・・基本クラス ・・例外基本クラス メモリ不足例外クラス 非サポート例外クラス ・・アーカイプ例外クラス ファイル I/O 例外クラス リソース確保例外クラス ・基本クラス BYTE の配列 ・ WORD の配列 DWORD の配列 ・ void * の配列 CObject の配列 ・ CString の配列 ・・ TYPE クラスの配列 ・・ void* の双方向リスト ・ CObject の双方向リスト ・ CSt 「 ing の双方向リスト ・・ TYPE クラスの双方向リスト ・ WORD から void * への写像 ・一 void * から WORD への写像 void * から WORD への写像 ・ CString から void * への写像 ・ CString から CObject への写像 CString から CString への写像 ・ WORD から CObject への写像 KEY から VALU E への写像 Fig. 22 MFC ファイル I/O , アーカイプクラス ・・アーカイプ作成・展開クラス ・オプジェクト内容表示クラス 基本クラス 低水準ファイル I/O クラス ・・高水準ファイル I/O クラス メモリファイル I/O クラス ・ファイル状況構造体 ・・実行時クラス情報 ・メモリチェッククラス CA 「 c h ive CDumpContext CObject L CFile CStdioFile CMemFile CFileStatus CRuntimeClass CMemoryState んて、からてないと , 定義も宣言もて、きません。 ass Library( 以下 MFC) と呼ばれるクラスラ さは約 16K バイトて、した。比較のために , 同 ②メンバ関数のあるローカルクラスを定義 イプラリが付属しています。これは , リス 様のプログラムを従来の C 言語による方法て、 ト , 配列 , 連想リストといった集合クラス 己述すると , 約 5K バイトて、したから , ライ できない や , 文字列や時間 , ファイルを高度に扱う プラリの最小サイズは 10K バイト程度と見積 関数の中て、定義されたクラスを , ローカ ルクラスといいますが , MS ー C て、は関数の定 ためのクラス , 前述した例外処理クラス , ることがて、きます。また , BorIand C 十十の 義を持つローカルクラスを定義て、きません そして , もっとも大きいものて、ある Windo ObjectWindows クラスライプラリを用いて ws の API 関数をカプセル化したクラス群な (List 25 ) 。 記述した場合は 48K バイトにもなり , MFC ライプラリはその 1 / 3 ~ 1 / 4 程度て、済むこと どから成り立っています (Fig. 20 ~ Fig. 2 なお , ローカルクラスには , いくつかの 4 , List 26 がその使用例 ) 。 ややこしい制限があるのて , もし将来サポ がわかります。 ートされたとしても , 扱いには十分注意し 1 三ロ ー WindowAP にそった設計 コンノヾクトな実装 MFC ライプラリの設計方針は , Fig. 24 の Windows クラスを見ればわかる ・実行スピード重視 かと思いますが , Windows のリソースに従 ってクラス階層が決定されています。おお ・最小コードサイズ むね , Windows の API 関数の第 1 引数がクラ ・ A 円との共存 スに相当しており , これまて℃言語て、 Wind ・ C 言語で書力、れたアプリーケー ションの移 ows アプリケーションを書いてきた人に馴染 植が容易 ・将来の拡張のための基礎 みやすいようになっています。 たとえば , GDI 関数なら , 第 1 引数に HD というものてす。実際 , ライプラリの大き さは全部あわせて 40K バイト弱と , たいへん C ( デバイスコンテキストへのハンドル ) を取 ります。これに相当するのが , CDC クラス コンパクトになっています。 List 27 のウインドウに線を引く極めて単 とそのメンバ関数てあり , Fig. 25 のように 純なプログラムては , 実行ファイルの大き 対応づけられます。したがって , Fig. 26 の 特集日本版 M ⅸ 0S0 代 C/C + + Ver. 7.0 の全貌 65 M ・ roso 化 Foundation ass Library MS-C7 には , Microsoft Foundation CI Fig. 23 MFC 構造体 CString CTime CTimeSpan CPoint CRect CSize ・・文字列 ・・時間 時間差 ・・矩形 幅と高さ

5. 月刊 C MAGAZINE 1992年12月号

The + 、を : せらかく C をおぼえたというのに , なんでまたしちめんどうくさい C 十十な : をーんそをおぼえるのか ? すばり「 C 十十はカッコいいから。といい切 0 てしまー 。をいましよう ( 笑 ) 。というわけで , めったやたらに「仮想」をつければカッコよ く聞こえる今日この頃 , 今回は C 十十における「仮想」なものを扱います。 わっていない言語はひとっとしてないて、し なのて、す。 バラスヴィーダヒ よう ( と , 思うんて、すが・・・・。浅学にして知 たとえば , 派生クラスを基本クラスへキ ことがあったらごめんなさい ) 。まる ャストすることなどはてきません ( そりゃ , 継承は , C 十十において非常に重要な概念 てジェイソンの出てこない 13 日の金曜日に キャストするオペレータとかコンストラク て、す。継承なかりせば , クリープのないコ タを定義すれば別てすけどね。 C 十十って何 なってしまうわけ。それにしても , いつも 不思議だったのは , ああ惨劇の起こること てもありだから・・・・・・ ) 。生まれたときにはい ーヒーどころか , コーヒー豆をがりがりか じっているに等しいくらいなものて、す。 のわかっているクリスタルレイクになぜ行 ろいろとご厄介になっておきながら , その まあそれが証拠には , 世にオプジェクト くんてしようかね。観るほうからいわせて あとはもう知らないよ , と。これじゃあま 指向言語をうたうもの数あれど , 継承の備 もらうと , 何が起こるかわかっていてそれ りに基本クラスが可哀想じゃありませんか。 をわくわくしながら待っているのて、すから , 別に感情にかられたわけてもありません アメリカ版水戸黄門だといえなくもないな が , 実は C 十十て、は基本クラス・派生クラス const メンバ変数 あと・・ の関係は , ある程度まて、融通の効くように クラスのメンバは , const あるいは static さて , 何が C 十十において継承を重要なも なっています。ポインタという仲介者を一 として宣言することができます。 度通すことにより , プログラムの側からは のにしているかというと , そのひとつには , const なメンバ関数は 10 月号のコラムで解 基本クラス・派生クラスが同じにみなせて 派生クラスが 説しましたが , もちろん const なメンバ変数 ポインタを通じて基本クラスとみなす しまう , という荒業があるのてす。 というものもありです。 ことができる もう少し詳しくいうと , 派生クラスへの class A { ことがあげられます。 ポインタ ( 今回は , 「クラスのインスタンス const int i : オプジェクト指向の観点からすると , へのポインタ」という意味て「クラスへのポ インタ」という表現をしています ) はキャス の仕掛けによって C 十十は C とまったく異な トせずとも基本クラスへのポインタとして る外観を見せることになります。 扱うことがてきるのてす (List 1 ) 。また , 参 基本クラスと派生クラスの 照も C 十十の内部てはポインタの亜種として 怪しい関係 実装されているのて , 同じように OK てす。 このことにより , 基本クラスへのポインタ もともと基本クラスを継承して作った派 生クラスは , 別の情報を含んている新しい あるいは参照を引数にとるような関数は , コードを 1 行も変更することなく派生クラス 型てす。てすから , 本来ならば基本クラス とは親子関係以上の深い関わりはないはず のインスタンスを扱うことがてきるように 、第 6 回 ~ 仮想関数 コラム const というぐらいですから , 変数の値を 変更することはできません。唯一の例外が , コンストラクタです。コンストラクタから は , 初期化の意味で const なメンバ変数への 代入ができますただし i という 普通の代入文ではなく , という形でなければなりません。 C 十十入門講座 121

6. 月刊 C MAGAZINE 1992年12月号

d 加嚇解講座・Ⅷ 0 刪 WorId yshibata 氏の 98 ノヾージョンの djgcc は , 1. 07 に対応したレヾージョンのパッチが配布さ れています。レヾージョンて、のおもな変更箇 所は以下のようなものて、す。 ・ Cyrix Cx486SLC/DLC に対応 ・ Borland C 十十 3.0 に対応 ・バグの修正 386 マシンを安価にスヒ。ードアップするチ ップとして最近話題の cyrix CX486 て、すが , オリジナルの g032 て、はうまく動きません。 この問題を解決するパッチが 98 パッチに含 まれています。この部分のパッチは IBM - p C て、も有効なのて、 , IBM ー pc て℃ X486 を使っ ている方にとっても朗報て、す。 98 ノヾッチ ーナ ドキュメン 16 , 24 , 32 , 48 ビットの領域て、値を表現す うクラスを与えます。このライプラリには , Fix クラスは , 固定小数点表現の実数を扱 し , 複素数型と演算を与えます。 Language System 付属の complex に相当 Complex クラスの実装は ,AT&TC 十十 の翻訳を掲載します。 クラス , CursesWindow クラスに関する部分 ampleStatistic クラス , SampleHistogram x クラス , RNG クラス , Random クラス , S 載します。ここぞは , CompIex クラス , Fi ser' s Guide(libg 十十 . texinfo) の翻訳を掲 前回に続いて , GNU C 十十ライプラリ U る 4 種類のクラスがあります。 乱数関係のクラス・としては , RNG と Ran dom というクラス , そしてそれらからの派 生クラスがあります。 SampleStatistic クラスは , データ標本を 集め , それらを処理するためのものて、 , Sa mpleHistogram は , 標本化を行ったり , 結 果を表示したりするクラスて、す。 また , CursesWindow は , (UNIX て、 ) 文字 端末を制御するための関数群をクラスとし てまとめたものて、す。 この連載のページにかぎって , GNUG eneral Public License に基づいた再配 布を認めます。 GNU C 十十ライプラリ Use ド s Guide Copyright ⑥ 1988 Free So 負 Foundation, c. , 1992 Proceed, c. Doug Lea 著 / 株プロシード訳 複素数クラス ( Com ex ) Complex クラスの実装は , Stroustrup のものに似て いる ( * 1) 。クラス名は libg 十十の慣習にそって com が ex て、はなく Complex となっている。複素数の算術演算 子と関係演算子として十 , , ! = が提供されている。 ( 0 , 0 ) による 割り算は例外処理を呼び出す。 複素数の生成と使用法を以下に示す。 ・ CompIex x ; 初期値を設定しないて、 complex 型オプジェクトの 宣言を行う。 ・ Complex x 2 ; Complex y ( 2.0 ) ; x と y に複素数値 ( 2.0 , 0.0 ) を設定する。 ・ Complex x(), 3 ) ; x に複素数値 ( 2 , 3 ) を設定する。 ・ Complex u (x) ; Complex v u および v に x と等しい値を設定する。 ・ double real(Complex& x) : x の実部 (real part) を返す。 ・ double imag (CompIex& x) ・ x の虚部 (imaginary part) を返す。 ・ double abs (CompIex& x) ・ x の大きさ ( 絶対値 ) を返す。 ・ double norm (Complex& x) ・ x の大きさの自乗を返す。 ・ double arg (Complex& x) ; x の偏角 (argument) を返す。 絶対値 r て、偏角 t の CompIex 値を返す。 ・ Complex CO 可 (Complex& x) ・ ・ Complex polar(double r, double t = 0.0 ) ; x の共役複素数 (complex conjugate) を返す。 ( * 1 ) ただし AT&T の C 十十には付属していた FFT 関係 0 能は実装され ていない djgcc 詳解講座・ HeIIo GCC WorId 83

7. 月刊 C MAGAZINE 1992年12月号

く C 言語の本〉 秘伝 C 言語問答 ポインタ編 柴田望洋著定価 2 , 600 円 C : 98 スーバー ライプラリ 柴田望洋著定価 3 , 700 円 C プログラマのための レゴリズムと テータ構造 近藤嘉雪著定価 2 , 200 円 MS ー DOS の機能を拡張する C プログラミング技法 中島信行著定価 3 , 900 円 はじめて C を学ぶ人のた加 コンヾ弔レエラー 完全征服 田中庸介著定価 2 , 600 円 C 言語による 実践 MS-DOS プログラミンク入門 秋津彰文著定価 2 , 200 円 X68000 C プログラミング 中森章著 定価 2 , 600 円 定価は税込みです ソフトバンク出版事業部 C + 十入門講座 127 リ The ◆ Fig. 2 仮想関数を含むクラスの構造 class A int a , vptr A . vptr A ※この部分はユーザからは見えない class B int a , vptr B ・ vptr A . int b , vptr B ※この部分はユーザからは見えない 基本クラス A の fb ( ) こまでは CIass A と同じ構造 ここから Class B 固有の部分 派生クラス B の fb ( int ) は基本クラス A : fb(int) の fb (void) と区別される たとえば , 仮想関数てある fa ( ) の呼び出 しを仮想的に : ー ) 書き下してみると , st 「 uct と class 再び A * a 以前 , C 十十においては struct と class の違 a->fa() ; / / 以下のように展開される いはわずかにデフォルトのアクセス制御で //vptr fp a ー > fa ・ あるが , 両者は使い分けたほうがよい , と 書きました。 C と同じく struct はデータのみ という具合になります。基本クラス A てあっ を含むべきで , そうでないものを class にす ても派生クラス B てあっても仮想関数 fa ( ) へ るべし , と。 のポインタのあるオフセットは変わりませ 仮想関数が出てきたことで , その理由の ん。てすから , ポインタ a の指すものが , ク 一端を述べるときがきました。 Fig. 2 にみら ラス A の , あるいは派生クラスのインスタン れるように , 仮想関数を含むクラスのイン スてあるかぎり , 常に正しい仮想関数が呼 スタンスには , プログラマからは見えない び出されることになります。 要素が入ってしまいます。仮に , インスタ また , 派生クラス B におけるメンバの配置 ンスをそのままディスクへセープするよう が , 基本クラス A の配置の後ろにくつついて なプログラムを書いたとしましよう。ディ いることに注意してください。このことに スクに書いたデータは , そのままほかのプ より , クラス B が基本クラス A へのポインタ ログラムから使えるのでしようか ? に変換されたとしても , なんら矛盾は生じ 否。プログラムが異なれば , 同じクラス ません。なぜなら , クラス B の一部としてク を使っていたとしても仮想関数のアドレス ラス A の構造をそのまま保持しているわけて は異なるはずです。それを無理矢理ディス すから。 クから読み込んだとすれば , 仮想関数への さて , 派生クラスてあるクラス B ては , 基 ポインタはむちゃくちゃなアドレスを指す 本クラスと同名の fa ( ) および用 ( ) というメ ことになり , 暴走してしまうことは必至で ンバ関数を持っています。 fa ( ) は基本クラ す。 スの同名の関数をオーバライドしていて , 仮想関数ポインタもクラス B のメンバ関数が ス A の用 ( ) は引数を持ちませんが , 派生ク 設定されています。 ラス B の価 ( ) は , int 型の引数を持ちます。 しかし , 用 ( ) はどうてしよう。基本クラ したがって , 引数の型 , 数が異なるため , コラム

8. 月刊 C MAGAZINE 1992年12月号

左のように従来記述されてきたものが , 図 の右のように簡潔に記述されることになり ます。 こて、 , CClientDC は CDC の派生クラス て、 , コンストラクタて、 GetDC を呼び出し , デストラクタて、 ReIeaseDC を呼び出すのて、 , CClientDC のライフタイムが , すなわち DC のライフタイムに対応づけられ , DC の管理 が非常に楽になります。 Windows がオプジェクト指向の設計て、あ ると主張されても , これまて、は納得いかな かったものて、すが , このように , C 十十のク ラスによるカプセル化がほどこされると , 少しはうなずく気持ちにもなれますね。 マイクロソフト社の発表によれば , MFC ライプラリは Foundation の名前が示すとお り , 将来提供される大規模な統合的クラス 体系の基礎となるものだそうて、す。楽しみ にしていましよう。 M FC の全体像 さて , MFC のクラスライプラリは大きく 分けて 2 種類あります。すなわち , Windows に関するものと , そうて、ないもの。後者は Windows 以外のアプリケーションプログラ ミングにも役立つものて、 , たとえば , 例外 処理やファイル I/O, 集合クラスなどがあて はまります。 Fig. 20 は , 集合クラスを示しています。 集合のタイプには , 配列 (Array), 双方向リ スト (List), 写像 (Map) があります。それぞ れ , すて、に , void * や CString, CObject, WORD などについて用意されていますが , 前述したテンプレート処理により , ューザ が定義したクラスについての集合クラスを 生成て、きます。 Fig. 21 は , 前章て、述べた例外処理用のク ラス CException て、す。 Fig. 22 は , ファイル I / O やアーカイプ作成・ CArchive は , アーカイプ作成・展開のた 展開のためのクラスて、す。 Fig. 24 MFCWindows クラス CObject CWinApp CMenu CDC CClientDC CWindowDC CPaintDC CMetaFileDC CGdiObject CPen CBrush CFont CBitmap CPalette CRgn CWnd CFrameWnd L CMD ℃ hildWnd CMDlFrameWnd CDialog CModaIDialog CStatic CEdit CButton CListBox CComboBox CScrollBar Windows アプリケーションの例 1 : #include く afxwin. h 〉 ・・基本クラス アプリケーションクラス ・メニュー テノヾイスコンテクスト ( DC ) クラス ・ウインドウのクライアントエリアの DC ・・ウインドウの全体の DC BeginPaint()C 得られる DC ・メタファイルと結合した DC ・・ G 引描画リソースの基本クラス プラシ ・フォント ・ビットマップ " ノヾレット 領域 ・ウインドウ , コントロールの基本クラス ・ウインドウの基本クラス M 引の子ウインドウ ・ M 団の枠ウインドウ ・・モードなしダイアログ モードつきダイアログ ・・文字列 ・エティットできる文字列 ・・ボタン ・リストホックス コンホホックス ・スクロールバー めのクラスて、す。 こて、いうアーカイプと List 27 2 : 5 : 6 : 7 : 9 : 11 : 16 : 17 : 18 : 19 : 20 : 21 : 22 : 23 : } 24 : 27 : 29 : 32 : 33 : 34 : 35 : 36 : } 38 : CTheApp theApp : re turn TRUE : m—pMainWnd->UxIateWindow() : m_pMainWnd->ShowWindow(m nCndShow) ; m—pMainWnd = new CMainWindow() ; 30 : B(m CTheApp: : lnitlnstance() BOOL lnitlnstance() : 26 : public: 25 : class CTheApp : public CWinApp { dc. LineTo(). right ー x, r. tmtom) ; dc.MoveTo(), の ; for (int x = 0 ; x く = 「 . right; x + = 5 ) { GetCIientRect(r) ; CRect r ; CPaintDC dc(this) ; 14 : void CMainWindow: :0nPaint() 12 : END_MESSAGE MAP() ON WM PAINT() 10 : BEGIN_MESSAGE MAP(CMainWindow, CFrameWnd) DECLARE_MESSAGE MAP() afx—msg void OnPaint() ; CMainWindow() { Create(NULL, ” LINES") ; } 4 : public: 3 : class CMainWindow . publ ic CFrameWnd ( は , 66 オプジェクトをバイナリの形式て、ディ C MAGAZINE 1992 12

9. 月刊 C MAGAZINE 1992年12月号

たかも誇示しているかのようて、す。 ということて、す。 indow クラスに対して付け加え , あるいは置 オプジェクト指向の参考書などには , ポ 一度仮想関数が呼び出されれば , 派生ク き換えて実現してやるのて、す。 リモフィズムとして紹介されています。 ラスて、新たに定義したメンバ変数には自由 Windows プログラミングては , ウインド にアクセスて、きます。 もちろん , 一から自分て、作りあげたクラ ウごとにその外見や性質などは大きく異な ス階層に対しても「こいつあ使える」仕掛け 通常のメンバ関数とは異なり , 仮想関数 るはずて、す。メインウインドウもウインド て、すが , 私たちの日常的なプログラミング においては戻り値だけが異なる同名の関数 ウの一種だし , ボタンだって実はウインド てオーバライドすることは許されません。 においては , もっと違う局面のご利益のほ ウて、す。 うが大きいかもしれません。 また , 引数の数や型が異なる関数は , オー ObjectWindows においては , TWindow バライドしているとはみなされず , 別の関 すなわち , 市販のクラスライプラリのコ という基本クラスてウインドウの持つ最低 ードを 1 行も変更することなく , 自分好み 数になります (List 5 ) 。 限の振る舞いを記述し , そこから先の独自 のクラスを作りあげること。 ほかにも ,static メンバ関数を仮想関数に の仕様は , 派生クラスを導出してそこて定 することはて、きません。あとて、詳しく扱い たとえば , BC 十十 & AppIication Fram 義してやることになります (List 6 ) 。ビット eworks を買うと , Windows 用のクラスライ ますが , 仮想関数の呼び出しにはインスタ マップを表示するなら , Paint ( ) という仮想 ンスの存在が必須条件だからて、す。 プラリ ObjectWindows がもれなくついてき 関数をオーバライドします 0TWindow の P ます。 ObjectWindows は , Borland が Win aint ( ) メンバ関数は何もしない関数として 仮想関数のご利益 dows 用クラスライプラリとして提供する唯 定義されており , 派生クラスて、オーバライ ーのクラスライプラリて、あり , 実際にアプ ドしてやれば , 思うがままの絵が描けてし 仮想関数と基本クラスへのポインタによ リケーションを作る労力のかなりの部分が まうわけてす。一度自分の派生クラスを作 って実現されるのは , ObjectWindows によって吸収されます。 ってしまえば , あとは ObjectWindows とい ・インスタンスを生成したあとは , それ しかし , Framework という名のとおり , うアプリケーションの枠組みが提供する , が派生クラスて、あるか基本クラスて、あ ObjectWindows は単にプログラムの枠組み 基本クラスと同じ管理に任せてしまえる。 るか意識せずにその提供するサービス を提供しているにすぎません。 名ラス機構の真骨項 , こにあり。 を利用することがて、きる 一例をあげましよう。 ObjectWindows に これは , クラスライプラリとしてはごく は , TWindow という , 1 個のウインドウに 自然な実装て、す。自然て、あるが故 , 無理な 対応するクラスがあります。 TWindow は く使いやすいものに仕立てることがて、きる 仮想関数のオーバライド まさに「単なるウインドウ」て、あり , TWind のて、す。 復習になりますが : 派生クラスで ! 基本 ow のインスタンスを作っただけて、はそのウ むしろ難しいのは , すっきりしたクラス クラスの関数を定義しなおすことをオーバ インドウには何も表示されません。 TWind ライプラリを作るために基本クラスと派生 ライドといいます。普通の ( 仮想でない ) メ ow は , あくまて、も Windows における「ウィ クラスをどう切り分けるか , いい換えれば , ンバ関数では , 戻り値の型のみ異なるメン ンドウ」を具体化したクラスにすぎず , 表 基本クラスにどこまて、の役割りを負わせる バ関数をオーバライドすることが可能でし 示 , マウスやキーポード入力への対応など か。そして , 派生クラスを導出したときに そこから先のことは個々のプログラマが TW いかに制限を少なくして効率のよいプログ ObjectWindows の例 コラム class Base { , void f ( ) : ー 0 List 身 変 に 保 タ 確 ン イ トピ ポ スコ の クるに ス テす面 ラ ン択画 ク コ選 / 本 スを / 基 イプ デマ リト末 モッ始 メビ後 0 れし A 0 1 上 0 十レ . ーー .00 11 十》 , -P ・ 1 ↑ ・ 1 0 ・ 1 0 十》り O 0 0 0 0 O - + し 0 + レ O O 0 ・ 1 0 ・・ ( 、・ 1 E 一一 0 11 + レ 0 ・ 1 ・ 0 E* 、 0 《し E 1 よ +> 1 よ ・ 0 Q) O ・ 1 0 0 1 ・ O A ・ 1 ア↑・ 1 よワ 0 CO -4- LO の 0 叮ー 8 9 0 1 ー - りっ 0 -4- 広 ) れ 0 1 ↓ 1 よ 1 よ 1 よ 1 ・ - 1 よ・ー - 11 class Derived charf(); . public Base { / / OK しかし List 5 に見られるようにチ仮想関 数ではこれは許されません。仮想関数では , ・引数の数 , 型が同じであれば戻り値の 型も同じでなければならない という約束があります。 124 C MAGAZINE 1992 12

10. 月刊 C MAGAZINE 1992年12月号

リ The 0 + C ならこうかな こりや駄目だ・・ List LiSt 1 : #include く stdi0. h> 2 : enum A—type { CLASS—A, CLASS—AA } type ; 5 : A() { type = CLASS-A; } void he110 ( ) 8 : 9 : printf( ” A: : he110 ( ) \ n ” ) ; 10 : 11 : friend void cal l—hel 10 ( A * ) ; 12 : 14 : 15 : class AA : public A { 16 : public: AA() : A() { type = CLASS-AA; } 17 : void he110 ( ) 19 : printf( ” AA: : he110 ( ) \ n ” ) : 20 : 21 : 23 : 24 : void cal 1 ー he110 ( A * ap) switch (ap->type) { 26 : case A: :CLASS A: ap- 〉 hello て ) : break; 28 : 29 : case A: :CLASS_AA: ((AA*)ap)- 〉 hello() ; break; 30 : 32 : } 33 : 34 : void main() 36 : A* ap = &a; call—hello(ap) ; 38 : 39 : AA aa; 40 : ap = &aa; call—hello(ap); 41 : 42 : ) 仮想関数のオーバライド 1 : class Base { virtual void a(); 2 : 5 : class Derived : public Base { / / 戻り値の型のみ異なるのでエラー char a(); 6 : / / void a() とは別関数 void a(int) ; 1 : #include く stdio. h> 3 : class A { 4 : public: void he110 ( ) : 5 : 7 : 8 : void A::he110() printf( ” A: : he110 ( ) \ n ” ) : 10 : 12 : 13 : class AA : public A { 14 : public: void he110 ( ) ; 16 : } ; 17 : 18 : void AA::heIlo() 19 : { printf( ” AA: : he110 ( ) \ n ” ) ; 20 : 22 : 23 : void main() 24 : { 25 : 26 : A* ap = : ap->hello() ; 28 : AA aa ; 29 : ap = &aa; ap- 〉 he110() ; 30 : 仮想関数の宣言 1 : class A { virtual void func(); LiSt List 〃仮想関数 後述するように通常と異なる呼び出し方に えば , 正しい関数を呼び出すための煩雑な 仮想関数とは , 基本クラスが先々を見越 なり , 基本クラスへのポインタへ姿を変え 手続きは , すべてコンパイラがめんどうを たあとも , 正しいメンバ関数の呼び出しが この関数は派生クラスでオーバライド 見てくれることになります。 case 文のオン パレードはもういらない されるかもしれないから , いかなる場 保証されます。 基本クラスて仮想関数てある , と宣言さ 合でも正しく派生クラスの関数が呼び このためのキーワードが , 出されるように れたメンバ関数は , 子孫末裔に至るまて仮 virtual てす (List 4)ö宣言の頭にⅵ「 tu 引がついたメ 想関数として認識されます。派生クラスて とコンパイラによろしくお願いするちょっ オーバライドしても , いちいち「 virtual 」を ンバ関数を仮想関数と呼び , 通常のメンバ と特殊な関数てす。つまり , 前述したよう つける必要はありません。もちろんつけて 関数と区別します。クラス宣言の外部て関 に派生クラスのインスタンスへのポインタ も構いません。たとえば , BorIand のクラス 数を定義する場合は , virtual キーワードは が基本クラスへのポインタに姿を変えてし ライプラリては , 派生クラスても怠りなく まったあとても , 正しく派生クラスの関数 つけません。 ⅵ rtual をつけて , 仮想関数てあることをあ を呼び出すためのものてす。仮想関数を使 仮想関数として宣言されたメンバ関数は , C 十十入門講座 123