スデコード以前の MC68000 内のレジスタ資源 現しています ( List 2 , Fig. 4 ) 。 word は同様 は Pentium からはリトルエンディアンでその に 16 ビットレジスタに対して 8 ビットの巡回 ままアクセスできます。 シフト命令 ror を使って変換しています。 工ンディアンの変換処理は C のコードでは EX68 はこのほかにもインラインアセンプラ オーバヘッドが多くなるため , EX68 ではイン で記述している部分が多くなっています。 ラインアセンプラで , Pentium に用意されて れは MC68000 と Pentium では基本的にインス いるロングワードのエンディアン変換用イン トラクションの互換性がないとはいえ , 似た ストラクション、 bsw 叩を呼び出すことで実 性質のインストラクションが用意されている List X68000 資源の定義 Fig. 4 bswap の動作 b31 b0 3 0 1 2 0 3 2 1 / / c 部内部資源 MPU 定義 class u{ pub ic: ULONG ー d [ 9 ULONG —pc,—ppcy—USP'—ssp; //,—cyclecount'—lastcyclecount; //,—thiscycle; union { ULONG —sr,—psr; struct {UBYTE ー s て 0 , ー srl ! ー s て 2 , ー s て 31 ー s て 4 , ー s て 5 , ー sr 一 sr U{ONG ー土 ns セー p 辷て ULONG new—adr; U も 0 製 G last—access; struct { ULONG flag ・ ULONG levef; ULONG vector; U も 0 製 G reqbit; み み込一一 込み 読の の C5 0 0 0 ・ , tn O = 義 0 0 ・エ 0 0 ・ ( Cn tO 一ーー = の O O O にしじセー 1 tn ・ O 、 0 、 0 ー 1 ー 1 ーエをエー 1 マ tn tn ュて を 4 41 ′ 0 0 ) inter ー struct ( ULONG åbase; ノ / ULONG base; ULONG count ー / / 00 ー土北ー 0Y0 厄島動用貢順カウンタ ULONG frame; ULONG cur デ ULONG next—int—cyc ー e ー 〃次の割り込みのべクタ ULONG next—int—vect ー )cycle; ÜLONG func-code; ULONG rte ・ void reset( void init( ) ・ mpu( ) { 加土 t( / / X68000 資源を定義する class x6 ( public: UBYTE *mmem ・ mainsxze; ULONG 土 0 ーて eq ー ÜLONG ULONG system—req ー 土 n セ inner—L0 ー cfg—opt cfg—opt ー class UBYTE tme 爪 [ 512 * 1024 十 32 ] ・ gme 爪 [ 1024 * 1024 十 32L UBYTE class clk clk; class spr spr; class joy コ OY ー class てし c てし c ・ class midi mi 土・ class pal pal; class crtc crtc; class dma dma ・ class adpcm aåpcm; C lass sram; C lass SCC scc; class fm fm; class m p m p,• class fd fd ・ class vid VI Class class timer timer; elass wav; class key key; C ー ass mouse mouse ー class wdrv wdrv; ULONG prev—mode; int cur—focus ー in し a00 ー—fate ー spr, d-sprbg0 , d-sprbgl; —text 土北 d ー 9 て ap 4 土 f PERFORM class perf perf; #endif 土 n し vsyne—up ー UBYTE umem [ SRAMSI ZE 十 8 ] 引 UBYTE update ・ void load( リ c lass pal { public: union ( USHORT 加 em [ 256 十 256D ー UBYTE ー me 爪 [ 1024 pal( ) { u よい class sys { public: UBYTE て eg - [ 16 ULONG contrast; 土 n に power—seq ・ sys ( ) {power-seq= 6 羶 0 ・ CD ・ E O へ 1 」 十 1 」ー、・ , 編して ス 10 X ′、してセ 物 0 ・ー E て -0 -0 ー、 Z -0 ー 1 0 ・ 0 0 0 0 0 、 1 ー 1 、よ O よよ、よ 一ド - ー′ O 一、ま 0 よムよ 4 ー 0- 「 ( 、よ 0 //IM 42 C MAGAZINE 2000 7
標準ェラーに出ています。 Windows/DOS は標準ェラーをファイルにリダイレクトでき ないので , この 2 行はわたくしが手書きで書 き加えたものであります。ああ , 情けなや ( 付録 CD - ROM 収録の ER. C を参照して下さ い ) 。ソースファイル群の構成ではなくて , 個々のソースファイルの内容・・・・・・どこをど う書き換えたか・・・・・・を把握するためには , cvs のサプコマンド di Ⅱを使用します : cvs diff d ⅲサプコマンドの出力は List1 のとおり です。この d の出力の読み方の " 初歩 " を解 説しましよう。行頭の・く ' は削除された行を 表します。逆にゞは新たに加わった行です。 そしてたとえば 6 行目の 7a8 , 11 は「 7 行目以 降に 8 ~ 11 行目の計 4 行が加えられた ( add さ れた ) 」という意味です。 a は add , c は change ( 変更 ) , d は削除 (delete) を表します。 Hash tabIeWithSorter.java のようなサポート的な クラスは , ファイル冒頭のコメントをわず かに書き加えただけですからご覧のような 簡単な d 沮出力になっています。 11 行目以下が , 肝心の JIndex.java に対す る変史箇所を報告している部分です。 17 ~ 20 行目は新たに加わったコメント , 22 行目 は新設の JIndexUtiIs.java へ移したために削 除した変数宣言です。 23 ~ 26 行までは変更 箇所 : 。 ~ ' で区切ることにより「変史前」と 「変更後」を教えています。 30C33 は , 「旧 30 行が現 33 行に変わった」という意味です。 こでの変更はご覧のように , boole タイプ のフラグ変数に volatile という修飾子を加え ただけです。その意味は , 本稿前回のドジ 物語に詳しく々々々書かれておりますから こでは略します ( その結果 , この di Ⅱ出力 の 40 行目に見られるように , while 節の実 行文としての空文・ ; ' が復活しております・・・ ・・・そして n 叩 ( ) メソッドを削除 ) 。 今回は JIndex そのものの説明がほとんど お留守になってしまいましたが , CVS はプ ログラミングをする者ほば全員にとっての 強力な情報ツールです。次回はさらに本格 的に , CVS の上でのソフトウェア開発を試 42 : 147 , 150d150 M JIndex. java M Hashtab ー eWithSorter. java Fig. 10 update の出力 (1 ) ? JIndexUtils . class ? JIndexUtiIs. java ? test. セXセ ? MyJavaSwingApp$CloseFrame. class ? MyJavaSwingApp. c lass ? ReaderCmp. class ? JIndexLoc . class ? JIndexLocVector. class ? JIndexValue. class ? JIndex . class ? JapCharUti lities. class ? Ecmp. class ? Hashtab ー eWithSorter. c ー ass ? lities. java M MyJavaSwingApp. java Fig. 11 update の出力 (2) CVS. EXE update: use 'CVS. EXE add' tO create an entry for JIndexUtils. java CVS. EXE update: use 'CVS. EXE add' to create an entry fo て JapCharUtilities. java M MyJavaSwingApp ・ java M JIndex. java M Hashtab IeWithSorter. java 31 : 96C99 , 100 JIndexUtiIs. initJIndexUtiIs( / / reset flags inWord = gotNO = gOtTO = gotSpace = fal se; 27 : 37C40 26 : > vo ねセ e boolean doneAWord, itsNewWord, wordModified; 24 : く bo 引 e doneAWord, itsNewWord, wordModified; 23 : 30C33 く boolean inword, go に NO , go し TO , gotSpace; 21 : 20d23 19 : > Version 1.0 . 4 18 : 〉 CVSed 29 Apr. 2000 16 : 7a8 , 11 15 : diff - て 1.1.1.1 JIndex. java retrieving revision 1.1.1.1 13 : RCS fi le: C:%winCVS%CVSREPOS%JIndex%JIndex. java,v lndex: JIndex. java 9 : > nothing altered since 102 8 : > 1.0.4 fO て first CVSed srcs, 29 Apr. 2000 6 : 7a8 , 11 5 : diff - rl. 1.1.1 HashtableWithSorter. java retrieving revlsion 1.1.1.1 3 : RCS fi le: C:%winCVS%CVSREPOS%JIndex%HashtableWithSorter. java,v lndex: HashtableWithSorter. java diff サブコマンドの出力 1 List 1 : 2 : 4 : 10 : 11 : 12 : 14 : 17 : 20 : 22 : 25 : 28 : 29 : 30 : 32 : 33 : 34 : 35 : 37 : 39 : 40 : 41 : 43 : 44 : 46 : 48 : 51 : 52 : 53 : / / display current paragraph on a sub-frame 36 : 138 , 139C142 , 143 nop( / / C language's simple doesn't work: ??JLS 14.5 / / use fO て do-nothing op void nop( ) { / / 104 doneAWord is now volatile, no need fo て nop( ) く = = 中略 ( メソッド n 叩 ( ) が削除された ) ー く 47 : 184 , 198d183 く / / reader for katakana is automatically hiragana 49 : く static final char KKdiff = ( ' ア ' - ' あー 50 : く String katakanaToHiragana( String word ) { = 中略 ( メソッド katakanaToHiragana( ) が削除された ) = = 以下約 200 行略 = = 行していきましよう 156 C MAGAZINE 0 2000 7
S List3Foo. java / * も土 gt3FOO. java * / public class も土 s セ 3F00 { / / 空 List3Ba 「 .java system ・ out.println( "Bar; hellO ”十 s); public void he110(String s) { pub は 0 c lass List 3Bar extends も土 s し 3F00 { / * List3Bar. java * / List8Baz. java public c lass List3Baz extends 石土 s ヒ 3Ba て / * 乙土Sし3Ba2. java * / 〃空 List3.java publie class List3 / * L 土 s し 3. java * / List3Bar myBar ま new List3Baz( 〃 B の変数に Baz のインスタンスを代入 は 0 static void main(StringtJ args) ( myBar. ぬeⅡ0( ″ WO ⅵ d ″ IS List3Ba 「 . java 変更その 1 Java フ恤クラミングリファレンス 詳説川 K 解体新ロ メソッドの検索手順 説明を簡単にするために , 以下のように記号を定義する。 C : 議論の対象とするメソッド呼び出しが行われているクラス m : コンバイル時に決定した , 呼び出し対象メソッドの名前 T : コンパイル時に決定した , 対象メソッドが定義されているクラス X : 実行時の呼び出し対象オプジェクトへの参照 日 : 実行時の呼び出し対象オブジェクトのクラス ( 1 ) 起動モードが static の場合 呼び出し対象オブジェクトは不要であり , クラス T の m を呼び出す ( 2 ) さもなければ , 呼び出し対象オブジェクトが必要である それへの参照が n ⅶであれば , この時点で NullPointe 「 Exception を投ける 呼び出し対象オブジェクトへの有効な参照が得られていたら , その値を this に設定する ( 3 ) 起動モードが non ⅵ「 tu 酬すなわち , p 「 ivate 属性を持つメソッドを呼び出す ) 場合 ( 4-2 ) 起動モードが supe 「の場合には , S の初期値は C のスーパークラスになる クトが配列である場合には , 日には Object が与えられている ( 4-1 ) 起動モードが inte 「 face あるいはⅵ「 tu 引の場合には , S の初期値は日になる。ただし対象オブジェ ( 4 ) さもなければ , 以下のようにサーチ対象クラス S の初期値を定める この場合オーバロードは許されないので , クラス T の m を呼び出す ( 7 ) さもなければ , NoSuchMethodE 「「 0 「エラーを投げる へ戻る ( 6 ) さもなければ , S にスーバークラスがあれば , S の値をそのスーパークラスで置き換え , ステップ ( 5 ) 動しこの検索処理を終了する。 ( 5-2 ) 起動モードがⅵ「 tua て , クラス S において X , m をオーバライドしていれば , そのメソッドを起 ( 5-1 ) 起動モードが supe 「あるいは inte 「 face であればそのメソッドを起動し , この検索処理を終了する れた値に一致するメソッド m が宣言されていた場合には以下のいずれかを行う ( 5 ) クラス S において , 非 abstract で , メソッド名とシグネチャおよび返り値型がコンバイル時に定めら / * List3Bar. java 変更その 1 * / public cläss List3Bar extends List3F00 { / / 空 List3Baz.java 変更その 1 List8Ba 「 .java 変更その 2 system. out. println("Baz: helIO public void he110(String s) { p リ b は 0 class List3Baz extends s し 3Ba て { / * いSt3齟2. java 変更その 1 * / / * 缸Sし3F00. java 変更その 2 * / public class も土 st3F00 { public void hello(String s) system. out. Pて土nしーnにF00: れ e 日 0 ”十 s); 旧 List8Baz.java 変更その 2 / * List3Baz. java 変更その 2 * / / / 空 public class List3Baz extends List3Bar { Fig. 10 List 3 のサンプルラン D:%cmaga%javaref>java List3 he Ⅱ 0 worl d ar で終了するにもかかわらず , その間に該 メソッドが定義されていた型である List3B である List3Baz から始まり , コンパイルに ッドのサーチは実行時のオプジェクトの型 にエラーになっていた。なぜならば , メソ 更を行った場合メソッド呼び出しが実行時 ろうか。実は JDKI. 0 Ⅸでは , そのような変 義をクラス List3Foo へ移動させたらどうだ 正してそれぞれ List3g , 3h とし , hello の定 逆に , 元のプログラムの List3a , 3b を修 動した he Ⅱ 0 が呼び出される。 るだろうか。この場合 , 支障なく Baz へ移 当メソッドが見つからないからである。 しかし , JDK 1.1 からはクラス階層を Ob ject までさかのばって検索するように修正 されたため , 現在の処理系では List3Foo で 定義されたメソッド he Ⅱ 0 が起動される。 実際のコード呼び出し 最後のステップとして , 必要なサイズの ( スタック ) フレームが生成され , 実引数の 評価結果が ( 「メソッド呼び出し変換」を施 されたうえで ) 仮引数に代入され , 実際の コードが実行される。ただし , 該当メソッ ドが "synchronized" であれば , 起動前に特 定のオプジェクトにロックをかける。その 結果 , 現スレッドはロックに成功するまで プロックされることがある。 Java プログラミングリファレンス詳説 JDK 解体新書 117
[ 原因 ] にわかりますが , コンストラクタが 8 回起 で問題が発覚しても , こんな変なクラスを 動するのに対し , デストラクタは 1 回しか コピーコンストラクタと = 演算子を使っ 作ったやつが悪いのだ , にされるからです。 起動しません。つまり 7 個ぶんのオプジェ たプログラムは同じだと勘違いしているた あらかじめトラブルを避ける意味で「使わ クトが解放せずに残り , メモリリークを引 れたくない暗黙のメンバ関数を未定義にす め。 き起こすわけです。正しくは [ 対策 / 予防 ] る」のもひとつの策でしよう。 「 delete [ ] theT; 」 勉強しなさい : - ) 。 [ 例外 ] と記述しないといけません。 ニ重に de te する なし。 深刻度☆☆☆ コピーコンストラクタと [ 備考 ] 代入演算子を混同する List 22 のようなプログラムで (A) と (B) [ 症状 ] 深刻度☆☆☆ が , まったく同じ挙動だと勘違いしてはい 移植性が低く , 不可解なバグに悩まされ [ 症状 ] けません。というのも , 「 TestClass theB = t ます。 プログラムが期待したとおりに動きませ heA; 」と WTestClass theB; 」と「 theB = theA; 」 [ 原因 ] の合成ではないからです。つまり「 TestClas 解放に関する考えが厳密でないとか , s 市 eB ( theA ) ; 」と同じでコピーコンストラ 重に delete することが悪いということを知 = が書いてあっても代入演算子とはかきらない例 クタが起動します。これは自分で実際に らないためです。 Tes ℃ lass に表示ルーチンを仕込んで確認す [ 対策 / 予防 ] れば簡単にわかります。 二重解放を防止するための工夫の導入 これは個人で開発する場合は自分で無意 や , 二重 delete が悪いことを勉強してくだ 識のうちに避けて通り , 結果的に弊害に出 さい。 会わない率が高いかもしれませんが , 誰が [ 例外 ] 参加するのかわからないチーム開発では致 なし。 命傷になりかねません。なぜなら他人は , [ 備考 ] コピーコンストラクタと代入演算子の混同 これも OJT 的感性で起きる現象で , 「バケ がどうのこうのという心配よりも , 自分の ツでウラン」症候群のひとつです : - ) 。二重 保身や納期を守るほうが大事だから , いち delete を防ぐために delete した変数に強制的 いち気にしなかったり注意を払わず , あと に NULL を代入しておくという方法は , わ 基底クラスのメンバ変数がうまく設定できない例 clasg TestCIass ( char* mText ー public: TestCIass( ) { mText = NULL; TestCIass(const char* inT) { mText 号 strdup(inT); 嵭 virtual -TestCIass( ) { delete mText; const char* Get( ) const ( return inText; わ class ExTestCIass : public TestClass { int mLen; public: ExTestClass( ) { mLen = 0 ・ ExTestCIass(const char* 土れ T ) ( mLen = 。 : :strIen(inT); 土 n し GetLen( ) const { return mLen;• ん。 List 22 TestClass theA(*TEST"); TestClass theB ま theA; nestc lass theA ("TEST" リ TestClass theB; theB ま theA; List 23 ニ重 del ete 防止策 delete mptr; mPtr = NULL; List 24 List 不定値で d ete が起きる例 cl ass TestC ね ss { char* mText; public: TestCIass( ) { TestClass(const char* inT) ( mText strdup(inT); -TestCIass( ) { delete mText; void Set(const char* inT) { delete mText; mText = strdup(inT) デ const char* Get( ) cons に ( return mText; 冖 . ( 中略 ) を .. static void TestMain (void ) TestC lass theT2 00000.00 、 000 : cout くく theT2. Get( ) くく endl; static void TestMain ( VOid ) ExTestClass theT("TEST"); cout くく theT. Get() くく d 嵭 / / ←セットしたはずの” TEST ″が表示きれない 26 C MAGAZINE 2000 7
十五ロ の れたせいなのですが , もしも最初から特定 ないかもしれませんが , なるべく使われた プログラムの挙動がおかしくなったり , そ のメンバ関数をオーバライドするつもりも の後始末に追われてしまいます。 くないメンバに蓋をしておくべきでしよう。 なく使用禁止にしたい場合は , private 継承 [ 原因 ] [ 備考 ] を使うか ( ただしこの場合 , 使ってほしい 個人で開発している場合は , どれをどう 実は個人で開発しているぶんには起こり 基底クラスのメンバ関数があった場合 , め 使うかはその個人が把握しているため , にくいのですが , チーム開発で発覚しやす んどうくさいことになるのですが ) わざと pr の項目が禁じ手であることにピンとこない い現象です。チーム開発の恐ろしさをわか ⅳ ate メンバにし , 実装も省略することです っていないことが根本原因です。 かもしれません。 田 stll ) 。 List 4 の CountPeople を継承したクラスを [ 対策 / 予防 ] この現象は個人で開発している場合に 作る例で説明しましよう。 CountPe 叩 le は 性別による人数をカウントして合計を求め は , あまり問題になりにくいものです。チ f ⅱ end を悪用する るものですが , 性別不明の場合を考慮して ーム開発特有の落とし穴を認識し , それを 深刻度☆☆☆ 避けるよう配慮しましよう。つまりたいて いないので , 新たに ExCountPeople という [ 症状 ] いの人は , 各自が理解する範疇で仕事をし クラスを継承によって作ってみます。たと 書き換えによるバグが発生していること ているので , 注意を怠らないようにしない えば List 9 のように がわかっているのに , その場所を特定しに これで , うまくいくだろうと思うと大間 と , 勝手に自分に都合のいい解釈をして , くくなります。結果的にプログラムの挙動 違いで , List 10 のような使われ方をされる こちらの思惑から外れたことをする危険が がおかしくなったり , クラスのバグ取りや あるのです。 とうまくいきません。 バージョンアップがしにくくなります。 この場合 , 最後の合計の計算がおかしく [ 原因 ] なります。原因は Reset をオーバライドし忘 個人で事情がわかっている場合はかまわ 無節操に end を使うと弊害が起きるこ ExCountPeople とをわかっていない。というか , オプジェ クト指向の基本理念をわかっていないので す。 [ 対策 / 予防 ] 勉強しなさい : - ) 。 [ 例外 ] なし。 [ 備考 ] ある意味 , 皮肉な end という名前は , 使われてほしくないメンバ関数の対応例 class ExCountPeopIe ! public CountPeople ( 土北 mUnknown; ツ / 性別不明の数 ーノ / ←わざと p て土 vate メンバにする void Reset(); 〃もちろん中身を記述する必要もなし pub は 0 : ExCountPeopIe(){ mUnknown の [ 例外 ] List -CIass ÉxCountPeopIe : public CountPeople { 。 int mUnknown;a 〃性別不明の数 public: ExCountPeopIe() { mUnknown = の int GetTotal (void) 00 れ st { return mtJnknown 十 CountPeople::GetT0tal ( リ え n し GetUnknown(void) CO s し { return 、 mUnknown; リ void AddUnknown(int inAdd) { ( inAdd 〉 = 0 ) { mUnknown 十 = 土れ Ad )else{ th て OW CountPeopleException(); List List ExCountPeop を間違って使われる例 static VOid TestMain ( void ) ExCountPeop theCounter ー theCounter. AddFemale(1); thecounter. AddMale(2); theCounter. AddUnknown(3); 00 くく "female ”くく thecounter. GetFemale( ) くく endl; くく theCounter. GetMale( ) くく endl; cout く ma ー e theCounter. GetUnknown( ) くく endl; cout くく "unknown cout くく” tO a い = ” << theCounter. GetTotal ( ) くく endl; thecounter. Reset(); / / ←これは ? ! theCounter. AddFemale(1); theCounter. AddMaIe(2); cout くく fifemale ”くく theCounter. GetFemale( ) くく endl; = ”くく theCounter. GetMale( ) くく endl; cout くく 00u しくく” tO し a ー = ”くく thecounter. GetTotal( ) くく endl; List 暗黙のうちに用意されるメンバ関数 c lass Hoge ( public: Hoge( Hoge ( const Hoge& Hoge ( Hoge& operator= ( const Hoge& 〃デフォルトコンストラクタ / / コピーコンストラクタ 〃デストラクタ / / 代入演算子 プログラミングの禁じ手 21 特集 1
見られます ) 。しかしクラスを使わされる ほうはたまったものではありません。豪華 絢爛なメンバのうち , どれが自分の役に立 つものかを探すのがたいへんですし , さら にクラスが継承をしている場合 , 基底クラ スも探さないといけません。もちろん , の基底クラスも豪華絢爛であることはいう までもないでしよう : - ) 。 サイズの大きいメンバ変数を持つ クラスで自動変数を作る 深刻度☆☆ [ 症状 ] スタック領域に余裕のない組み込みやマ イコン , パソコンでは , プログラムのハン グアップや停止が起こります。 [ 原因 ] 余裕のない環境が存在することを知らな い , いわば無知によるものです。 [ 対策 / 予防 ] 走行させようとする環境の制約や限界を よく知ることです。 [ 例外 ] 仮想記憶で対応できたり , 余裕のある環 境ならよいでしよう。ただし移植性は低く なります。 [ 備考 ] 最近はマイコン , パソコンといえども走 行環境が充実してきましたが , だからとい って無限にメモリや記憶領域があるわけで はありません。これは C 言語編の「巨大な領 域を静的に確保する ( 2000 年 4 月号 45 ペー ジ ) 」のバリエーションでもあります 複数のメンバ関数をややこしく 利用する仕様にする 深刻度☆☆☆ [ 症状 ] プログラムが思ったように動作しなかっ たり , ノヾグがまぎれ込みやすくなります。 また , いったん完成したプログラムを修正 しようとしてバグが人ることがあります。 ややこしくすることが賢い証明だと勘違 16 C MAGAZINE 2000 7 [ 原因 ] いしていたり , ひとつの ( あるいは少ない ) メンバ関数でひとつの作業を完了させよう という意識がなかったり , 方法を知らない ことによります。 [ 対策 / 予防 ] ひとつの ( あるいは少ない ) メンノヾ関数で ひとつの作業を完了させるように設計しま [ 備考 ] なし。 [ 例外 ] しよう。 うになることも意味します。また複数のメ が多くなり , あとから検証するのがめんど はなく , メンバ関数の使い方の組み合わせ は量が多いから見通しが悪いというだけで メンノヾ関数が多いのは弊害ですが , それ 2 virtual -CaIIBack( CallBack( ) { mMisc = NULL; public: void* mMisc ー protected : class CaIIBack { class CFi leCopy { メンバ関数を減らす解答例 ンバ関数を操ってひとつの仕事をさせると いうのは一見「高度な」ことをやっていると いう錯覚にとらわれそうになりますが , た いていは設計不足や勘違いした自己満足 , あるいは仕様がよく固まっていないので自 己防衛的に複雑なメンバ関数仕様にしてい るなど , いろいろ考えられます。 何度もいうようですが , 複雑なものを作 ったり検証せざるをえなくすることは「賢 い」証明にはならず , 未熟である証明にし かなりません。 List 1 はひとつの作業 ( ここではファイル のコピー ) をするだけのために , わざとやや こしい仕様にしたものですが , こんなこと をするぐらいならひとつのメンバ関数で済 ませ , 実行する順番を間違えたり必要なメ virtua ー b00 ー UserCance Ⅱ void* ) = の / / コヒ。ー中のユーザキャンセルの 〃入力受け付け virtual void DirDown(const char*,void* ) = / / ディレクトリ降下 virtual void DirUp(const char*,void*) = / / ディレクトリ上昇 virtual ¯CFileCopy( CFiI ecopy ( public: ioMisc; } , void SetMisc(void* ioMisc) { mMisc virtual void FileCopyEnd(const char* ,void* ) virtual VOid FileCopyStart(const char*,void*) ・ / / Misc 引き数のセット / / コピー終了 = の / / 1 つのファイル / / コピー開始 ー / / 1 つのファイル MyFiI eCopyCa Ⅱ Back theca Ⅱ Back; CFi IeCopy theFC; static VOid TestMain(void) virtual VOid FileCopyEnd(const char* ,void* virtual VOid FileCopyStart(const char* ,void* virtual void DirUp(const char* ,void* virtual void DirDown(const char* ,void* virtual bo 引 UserCance Ⅱ void* public: class MyFiIeCopyCaIIBack : public CFileCopy: :CallBack { int ExecCopy(void); / / コピーする 土 n し setparam ( const char* , const char* , b001, Ca Ⅱ Back* ) , ・ / / パラメータをセットする = theFC. ExecCopy ( ) ) { if ( 0 )else{ if( 0 ! = theFC. SetParam( nxxx", ” YYY" ,true,&theCallBack) ) {
J a フ恤クラミングリファレンス 詳説 JDK 解体新 呼び出し対象オプジェクトが特定されて パイルせずに古いバイナリを使うと実行時 static メソッドはコンバイル時に解決されている いれば , それにロックをかける。さもなけ にエラー (NoSuchMethodError) になる。 れば (static メソッドの場合 ) , ロック対象は Fig. 9 の ( 1 ) からもわかるように , 呼び出 P 「 ivate メソッドは仮想起動しない し対象メソッドが s ねⅱ c の場合 , それがどの 該当のクラスオプジェクトである。また該 F1g. 9 の ( 3 ) では , 起動モード "nonvirtual 当メソッドの実行が正常終了であれ中断終 クラスに属しているメソッドであるかはコ 了であれ , 完了したあとにはロックは自動 ンパイル時に決定されている。より正確に の場合の説明をしている。その影響を示す いえば , static メソッドの呼び出しコードが 的に解除される。 のが List 4 である。クラス F00 のなかで , メ 含まれているコンパイル単位をコンパイル ソッド b と zot を定義している。 baz は prote 注 . すべき動 した時点で , どのクラスに従属するメソッ cted であり , zot は private である。またこれ ドを呼び出すかが決定され , クラスファイ らを間接的に呼び出すメソッド callBaz と 以上の仕様からの帰結として , 呼び出す caIIZot を定義している。それから , F00 を ルのなかに指定される。したがって , 呼び 出される側のコンパイル単位で s ねⅱ c メソッ べきメソッドの解決については注意を要す extend したクラス Bar においても , 同様に ドを定義する位置をクラス階層の上に移 protected baz と private bar を定義している。 る動作が生じる。それらのいくつかについ て最後に簡単に触れておこう。 すような変更を行い , 呼び出し側を再コン そして , メソッド main のなかでは , シグネチャはコンバイル時に定まっている s 起動モード non ⅵ ua に起動モードⅵ「 tu 引 / * List5. javä * / * シグネチャはコンパイル時に定まっている class F00 ( public 0 土 d 20 日 in セ n, double x) { SY em. ou し prin れ ( ” F00. zo こ ( (int)* public void zot(byte b, do 曲地 x) ( system. out. printIn("F00.20日 (byte)" 十 b 十な public void qux(int れ , double x) { / * st4. java * / * 起動モードれ onv 辷 tu 引と起動モード v 近 t ー class F00 { protected void ) ( system. out.println( ” F00. baz っ private void 名 0t ( ) ( system. ou し . p て土 n 目 n ( ″ F00.20 セつ public void caIIBaz( ) { / / v 土て tu モード baz(); public void ca 日 20 セけ ( 〃れ 0 れ辻 u 引モード 20t ( リ 十れ十 % (double)" 十 x 十つ” ) ・ ( do 曲厄ド + x + 物尸わ ( do 面厄尸十十つつ・ system. 0uしPてin凵n(”F00. qux((int)" 十 n 十” class Bar extends を 00 { public void zot(byte b, int m) ( system ・ out.println("Bar. 20t( (byte)" 十 b 十 public void x ( 土北 n, dO x) { system. out. printIn("Bar. qux( (int)" 十 n 十 public void qux(byte b, do 面 le 幻 ( class Bar extends を 00 { protected void baz( ) ( system. out. println("Bar. baz 0 て土 va セ e void 20t ( ) ( system ・ 0uしPて土n日nに Bar. 20 し (int)" 十 m 十つ″ ( do 面厄尸十 x 十”ド system. out.println("Bar. qux( (byte)" 十 b 十第 , (double)t 十 x 十物尸リ 0 lass List4 extends Bar { public static void main(String[J args) F00 myFOO = new F00 ( ) ー て myBar new Bar( system. out. println( ” F00 つ myF00. caIIBaz( リ myFoo. callZot( system. out. println(" system. out. printIn("Bar"); myBa て . caIIBaz( myBar ・ callZ0t( system. out. println(* system. out. Pて土n凵nにF00 but holds a Bar" リ myFoo ま myBar; myF00. caIIBaz( myFOO. ca Ⅱ 20 日 c lass も土虻 5 { public static void main(string[ ] args) F00 myFOO ま new F00 ( ) ・ Bar myBar = new Bar( byte b ま 1 の system. out. Pて土n凵nにF00”リ myF00. zo 日 10 , 20 myF00. zot(), 20 リ myFOO. qux(10, 20 myF00. qux(), 20 リ System ・ 0uしPてin日れに system. out.println("Bar") 引 myBar. Z0t(10, 20 ) : myBar. zot(), 20 myBar.qux(10, 20 myBar. qux(), 20 潺 system. ou し printl n に system. out. println( ” F00 but holds a Bar"); myFoo = myBar; myFOO. zot ( 10 , 20 myF00. zot(), 20 myFOO. qux(10, 20 リ 叫 F00. q 皿 ( b , 20 ) を 1 18 C MAGAZINE 2 0 7
十五ロ の 0 ところで構造化というのは簡単にいい切 まで「手続き」レベルになるのが特徴であり , オプジェクト指向をさほど理解していな ってしまえば「プログラムコードの構造化 , それが同時に別の問題を生むことになるの いが構造化プログラムは得意な設計者の場 体系化」を意味します。複雑な手続きや関 ですが。 合 , 作ったクラスを見ると , これまた「ク 数を物理的 , 意味的にふたつ以上のモジュ 一方 , オプジェクト指向でいうクラス化 ラスを構造体と勘違いしている」の別バー ールに分割していく「分割統治」のひとつの は , 必要となるオプジェクトの抽出作業を ジョン , すなわち「クラスをモジュールと勘 例です。この場合 , 分割する基準は , あく したあとで , そのオプジェクトに必要な属 違いしている」パターンに陥っているケー 性やメソッドなどを付加していく作業です。 スがあります。たとえば List40 は極端な例 C + + で組み直した例 「何をオプジェクトとするか ? 」というのは ですが , これを見て違和感や「気色悪さ」を #include <stdio. h> オプジェクト指向プログラムでの中心的な 感じないとすれば , その人はクラスに対す c lass Anima に lass ( テーマですが , それだけに的確にオプジェ る感覚が発達していないのでしよう。ここ public: void Naku(void); クトを見い出せるようになれば , ほば , そ で使われている TextProcClass はどうやら文 void Aruku ( void の人はオプジェクト指向を理解していると 字列の処理をするつもりなのですが , よく 考えて差し支えないでしよう ( しかし , 見ると , 単なる文字列処理サプルーチン集 の「オプジェクトの見い出し」が難題なんで に成り下がっています。これだったらクラ すけどね : - ) ) 。 スにする必然性など何もなく , 各メンバ関 クラスをモジュールと勘違いしている例 class TextProcCIass 0 public: void Toupper(char* 土。で ext ) に〃文字列の大文字化 / / 冖を ( 中略 ) . 。を void Tolower(char* 土。で e ) ( / / 文字列の小文字化 〃冖中略 ) .. 土北 Length(const char* inText) { / / 文字列の長さをえる / た . コ中略 ) .. return ::strlen(inText); void Left(const char* inText,char* OutRes,int 土 n い ( //BASIC の LEFT$ と同じ void Copy(const char* 加 S てら cha て * ou に Des ら土土員いい″文字列の部分コビー Ⅳ 0 土 d AppendText(char* ioText,const char* inAdd) ( / / 文字列を追加する static void TestMain ( void ) TextProcCl ass theTextProc; char theProc[PROC—LENGTHl; char theWork[WORK—LENGTH]; 〃ワークから左 10 バイトだけを取り出し処理文字列にうっす / / 10 バイトない場合は足りない部分を空白で埋める 土北 theLen = theTextProc. Length(theWork); if(theLen > 10 ) { theTextProc. Copy(the?roc,theWork,10) ・ )else{ theTextProc. Copy(theProc,theWork,theLen); for(theLen 哥 10 ー theLen; theLen > の theLen-—) ( theTextProc. AppendText(theproc," 第 〃さらに処理文字列を大文字化する theTextProc. Toupper(theProc); List 39 class DogCIass : public AnimalCIass { public: void Naku(void); class catclass : public AnimaIClass { public: void Naku(void); ånt main ( void) / * 変数定義 * / DogC lass 20 CatCl ass *aCat; / * 手続き来 / aDog 哥 new DogCIass aCat = new CatClass; p て i f ( ”にゞ ) Naku! つ printf(hdog = aDog->Naku( printf ( "cat ー acat->Naku( p て土北 f ( ”に二 ) Aruku! の printf ("dog = aDog->Aruku( 嵭 printf (ficat = aCat->Aruku(); del ete aDog; delete aCat; て e し u てれ 0 ー List 40 void AnimaICIass: :Naku(void) pr 土 n ( ” ( て 0 て ) ” void AnimalClass: :Aruku(void) printf(nteku,teku,teku,teku. void DogCIass: :Naku(void) printf("wan,wan,wan... n ” void CatC lass : : Naku ( void ) printf( "Nyan,Nyan,Nyan ・ プログラミングの禁じ手 特集 1 35
を匯も終 パワップ ポインタ BorIand C + + 5.5 CompiIer 日本語版収録 協力 / インプライズ ( 株 ) C + + が持つ機能のなかには「壁」などと呼ばれ , 覚えるの が難しいといわれる機能があります。今回はそのなかのひ とっといわれている「ポインタ」について , やってはいけな いことやその利点を中心に解説します。 セスするにはポインタの前に・ * ' を付け ます。 int n = 3 ; int * ip; ip=&n;//ip は n を指す 〃読み出し int x = * ip; //x = 3 〃書き込み //n = 5 struct や class をポイントするときも同様 です。ポインタが示すもののメンバにアク セスするには " ポインタ→メンノヾ " です。 これは " ( * ポインタ ). メンバ " でも OK です。 class person { public: char name[32] ; / / 名前 int age; ″年齢 b00 語 du は ( ) { / / 成人なら true return age > = 20 ; C / C + + は語れないんですよね ポインタ はじめに ここしばらく XML にハマっています。 ポインタ ( pointer ) とは , その名のとおり いままでいろんなアプリケーションを書い ポイントする ( 指し示す ) ものです。ポイン てきましたが , そのほとんどが操作の対象 タは指し示すものの型に・ * ' を付けます。 となるデータをファイルに書き , そして読 み込むことで成り立っています。ファイル int 型や ch 型のポインタを作るなら , のフォーマットはアプリケーションごとに int* ip; //ip は int を指す 異なるものでした。しかし , これでは複数 char* cp; //cp は char を指す のアプリケーション間でデータをやりとり といった感じになります。これを , するとき , お互いのファイルフォーマット int * ip, に基づいた読み書きルーチンをそれぞれに char *cp, * ' 変数側に寄せて書いても 用意しなければなりません。 XML はデー のように かまいません。ばくは ip が " int へのポイン タフォーマットの標準です。アプリケーシ タ " であることを強調したいので , 型のそ ョンが読み書きするファイルが XML で表 ばに寄せています。ただし , この書き方で 現されていれば , そのファイルは XML 対 は複数の変数を・ , ' で区切って宣言するこ 応アプリケーション間で自由に交換可能に なるってわけです。ちょいと " わくわく " し とができません。 ません ? ってわけで , これまでに書いて int* a, b; きたアプリケーションを XML に対応させ は , ようと下準備の最中です・・・ int* a; それはそうとして , 今回はいささか憂鬱 int b; と解釈されてしまうからです。ばくは " 変 なお話を書かなきゃなりません。多くの 数の宣言は 1 行ずつ " の原則を守ることに C / C + + ビギナーが最初につまづく " ポイン しています。 タ " のお話です。ポインタなんてめんどく ポインタに変数を指し示させるには変数 さいもの , 使わずに済むのならそれにこし たことはないのですが , ポインタ抜きで の前に・ & を , ポインタが示すものにアク person adam , person * pp = &adam; pp->age = 25 ; //(*PP). age=25; でも OK if ( pp->adult() ) { cout << "adam は大人 " どーびてク″クのパワーアップ C + + 83
List Bad ー St 「 ing の障害を消す解答例 ( 2 ) List List 意味が込められています。日本語では「友 達」だからなんでしようが , 当然ながら「隠 達」ですが , 場合によっては「悪友」であっ 蔽」によるメリットが生かせなくなります。 たり単に友情に甘えてたかってきたり , 借 本来 end というのは , 演算子オーバロー 金の保証人になってくれと頼み込まれしば ドでの苦肉の対応であったと , 筆者は邪推 らくしたら多額の債務を残して蒸発してい しています。 た , という意味での「友達」になる可能性が ほかにもコンテナクラスをアクセスする あります : - P 。平気で自分の部屋にあがりこ ためのイテレータクラスの実装に使うなど , んだり , 持ち物をくすねても , そこは「友 限定された条件で使うなら end は便利な 用意していないメンバ関数が使われても支障がない例 CountPeop で暗黙に用意されているメンバ関数 static void TestMain(void) class CountPeopIe ( / / 女性の数 土 n し mFemale; countpeop 厄 theCounter;- / / 男性の数 ま n し噬引 e ー public: thecounter. AddFemale(1); theCounter.AddMale(2); countpeople(const Co 聞 eo が e & incp)( / / 暗黙のコピーコンストラクタ mFemaIe = inCP. mFemale; CountPeople theOther; mMale = inCP. mMale; theOther thecounter; / / わざと代入する cout ~ くく "female = を << theOther. GetFemale() くく endl; くく theOther. GetMale( ) くく endl; cout く male cout くくをし 0 セ a ーー = くく theOther. GetTotal( ーくく endl;e 00 面 tpeop 厄 theOther2(theOther)i7/ コビーコンストラクタを使う cout くく fifemale を theOther2. GetFemale() “ endl; < く theOther2. GetMale( ) くく endl; cout くく "male 00u0 くく物こ Ota ー = く theOther2. GetTotaI ( ) くく endl; ass Bad—String { char* mText; Bad-string ( const Bad-string& Bad—String& operator= ( const Bad—String& やは c : . を ( 中略 ) 、、を List List CO と peop 地 & ope て 0 て = ( 00 れ st countpeople& inCP)C 〃暗黙の代入演算子 mFemaIe ま、 inCP. mFemaIe; 噬 a 厄三 inCP.mMale; 工 e しⅡ rn *this ー 用意していないメンバ関数が使われて支障が出る例 List Bad ー St 「 ing で暗黙に用意されているメンバ関数 class Bad-String ぐ char* mText; public: Bad-String( ) { mText ま st て d 叩 ( ”第 Bad—String(const char* inT) { ÄText strdup(inT); Bad-string( ) { •s:free(mText) 引 void Assign(const char* inT){ . :free(mText); mtext = strdup(inT); const char* Get( ) cong しイ return mText; s ねセ土 c char* strdup(const char* inT) { char* theAns = static—cast く char*>( : :mal 0 ( ::Strlen(inT)o 十 1 ) ) if(theAns) ( : :strcpy(theAng,inT); て e に u てれ theAns ー class Bad-String ( char* mText; public: .. イ中略 ) 冖 Bad—String( CO れ s セ Bad—String& inBS ) { mText = inBS. mText7 Bad—String& ope て a セ 0 て = ( const Bad—String& inBS ) ( mText キ inBS. mText; return *this; static void TestMain (voåd) Bad—String theA( DThis is a Testw Bad—String theB ー theB = theA; / / 代入できる ! cout くく theB. Get( ) くく endl; theB. Assign("Assign(theB)"); cout くく theB. Get( ) くく endl; co くく theA.Get() 奴 endl; 〃あれ ? 内容が愛わっている。なぜ ? Bad—String theC(theA); 〃コピーコンストラクタが ! ・ cout くく theC. Get ( ) ーくく end 嵭 theC. Assign("Assign(theC)"); cout くく theC.Get( ) くく endl; co 此くく theA. Get(), くく endl; 〃あれ ? これも内容が変わっている。 Bad ー St 「 ing の障害を消す解答例 ( 1 ) 0 lags Bad—String ( char* mTexti Bad-String(const Bad—String& 土 S ) { mText = strdup(inBS. mText); Bad-gtring& ope て a セ or ま ( const Bad—String& inBS ) ( mText strdup(inBS,mText); return *this 22 C MAGAZINE 2000 7