virtual - みる会図書館


検索対象: 月刊 C MAGAZINE 2000年7月号
9件見つかりました。

1. 月刊 C MAGAZINE 2000年7月号

見られます ) 。しかしクラスを使わされる ほうはたまったものではありません。豪華 絢爛なメンバのうち , どれが自分の役に立 つものかを探すのがたいへんですし , さら にクラスが継承をしている場合 , 基底クラ スも探さないといけません。もちろん , の基底クラスも豪華絢爛であることはいう までもないでしよう : - ) 。 サイズの大きいメンバ変数を持つ クラスで自動変数を作る 深刻度☆☆ [ 症状 ] スタック領域に余裕のない組み込みやマ イコン , パソコンでは , プログラムのハン グアップや停止が起こります。 [ 原因 ] 余裕のない環境が存在することを知らな い , いわば無知によるものです。 [ 対策 / 予防 ] 走行させようとする環境の制約や限界を よく知ることです。 [ 例外 ] 仮想記憶で対応できたり , 余裕のある環 境ならよいでしよう。ただし移植性は低く なります。 [ 備考 ] 最近はマイコン , パソコンといえども走 行環境が充実してきましたが , だからとい って無限にメモリや記憶領域があるわけで はありません。これは 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) ) {

2. 月刊 C MAGAZINE 2000年7月号

standard ド文字コードを 8 ビットバイトの列で表現 同義語 ) のオプジェクトに文字を抽出 , 挿 テンプレート basic streambuf<wchar t> の べては , クラス wstreambuf( 特殊化された てへトへトになるかもしれない。仕事のす ド文字内部ストリームとの間の変換に疲れ いるプログラムは , そのコード表現とワイ 実装している。ファイルコードを理解して ファイル表現のすべてを扱う機能を完全に C + + ライプラリは , いままで説明してきた そろそろ本題に戻ろう。私が書いた標準 COde Conversion Facets コード変換面 [ 訳注 1 ] にとどめる。 ードが究極の解であることを強調しておく するという課題に対して , 多バイト文字コ 入する際に生じる。そのような操作は , オ プジェクト wc ⅲからの文字の抽出や wcout , wcerr, wclog ( すべてヘッダく ios 仕 eam > で定 義される ) への文字の挿入の際に自動的に 生じる。 これはこれでけっこうなことだが , デフ ォルトで何が生じるのかを知っておくのは いいことだ。 C + + 標準では標準 C ライプラリ で mbtowc , wctomb ( ともにく stdlib. h> に宣 言がある ) を呼び出す際に提供するものと 同等の変換を行うことを要求している。よ り正確にいえば , ライプラリは , 標準 C の 補遺 1 (Amendment 1 ) で追加されたこれら 関数の「再開可能 ( resta 血 ble ) 」なバージョ ンを用いるのと同等の効果を出す。これら の関数は mb れ owc , wcrtomb で , ともにく w char. h> で宣言されている。 これらの関数が何を行うかについては実 装に委ねられている。仕様では , ワイド文 字で用いるコード化規則と , ℃ " ロケール で多バイト文字に用いるコード化規則は文 書化することになっている。これが , ( すべ ての C + + プログラムが実行開始する際の「ク ラシック」なロケールの ) ワイド iostream ク ラスで用いられる変換である。 「 Dinkum C + + ライプラ屮訳注 2 ] を私自身が 移植したコンパイラにおいてさえ , 私はこ のコード化規則が何であるかを知らない」 という事実を知ると読者のみなさんは驚か れるかもしれない。シャーロックホームズ と同様 , 私はそれを知る必要はない。集中 したい仕事に無関係と思われる詳細を知れ という動きには抵抗する。最近までは , Ⅵ sual C + + が何をしているかについてを調べ る実験をしたことさえなかった。それを調 べたのも私のライプラリの実行結果に驚い た顧客の電子メールを受け取ったからにす ぎない。 調べた結果わかったことは , V1sualC + + は「口先だけ」方式を採用していたというこ Fig. 1 VisuaI C + + のヘッダのバグ 「 etu 「 n (fwrite (_Str->begin ( ) , Fig. 2 バグの修正 「 eturn (fwrite (_Str->begin ( ) , 1 , 単純な Unicode 変換機 List usmg namespace std; N, 1 , _File)= N, _File) = class SimpIe—codecvt : public Mybase { / / クラス Simp le_codecvt typedef codecvt<wchar—t , char , mbstate—t> Mybase; virtual result do—in(—St& —State, protected : : Mybase(-R) { } explicit Simple—codecvt(size—t —R typedef mbstate—t —St ー typedef char —To; typedef wchar—t ー public: virtua ー resu ー t dO ー ou に ( —St& —State , { return ( noconv } —E *—F2, —E *—L2, —E * & —Mid2) const const —TO *—FI ′ const —TO *—LI, const —TO * & —Mid1, const —E *—FI, const —E *—LI , const —E * & —Mid1, 1 10 C MAGAZINE 2 側 0 7 virtual int do—length(—St& —State, const —TO *—FI, {return (noconv); } —TO *—F2 , —To *—L2, —To * & —Mid2) const virtual result do—unshift(—st& —State, { return ( noconv } —TO *—F2, _TO *_L2, —TO * & _Mid2) const const —TO *—LI, size—t —N2) const —THROWO( ) {return (—N2 く (size-t) ( ユ 1 - —FI) virtual b0 引 do—always—noconv( ) const THROWO( ) { return ( true } virtual int do—max—length( ) const —THROWO( ) {return ( 2 ) virtual int do—encoding( ) const THROWO( ) { return ( 2 ) List 「自分専用記憶領域」ファイルを生成する const char *fname = "filename. txt"; / / or whatever locale IOC = —ADDFAC(IocaIe: :classic( ) , new Simple—codecvt); wofstream myostr; myostr. imbue(loc); myostr. open(fname, ios—base: :binary); if ( !myostr ・ is—open( ) ) くく tO ″くくくくー

3. 月刊 C MAGAZINE 2000年7月号

Standard これは , ロケールのカテゴリ ( 数値表現方法 , 文字集合 , 照合条件 ( co Ⅱ ation ) など , C 言 語 / POS Ⅸで規定された各国文化依存部分 ) のひとつひとつに対応しており , テンプレ ートの特殊化とクラス派生を利用して既存 カテゴリのカスタマイズ , 新規カテゴリの 拡張に対応するための仕組みを提供してい る。 else if (-state = return ( ok } [ 訳注 2 ] Plauger 氏自身が書いた標準 c + + ラ イプラリ List 少しまともな Unicode 変換機 1 12 / / クラス Fancier—codecvt class Fancier—codecvt : public Mybase { public: typedef wchar—t ー typedef char —To; typedef mbstate—t —St; exp は c 土し Fancier—codecvt ( size : MYbase(-R) 0 protected : ユ -R = 0 ) virtual result do—in(—St& —State, —E *—F2, ーを *—L2, —E * & —Mid2) const const —TO *—FI, const —TO *—LI, const —TO * & —Mid1, {*—Mid2 = * ー M 土 dl 十十 & 0xff; else if (—Mystate = break; } (—Ans = pa てし土 a 嵭 else if (—Mid1 十 1 = = —LI ) break; if (—Mid1 = = —LI Ⅱ -Mid2 = = —L2) fo て result —Ans = 0 新 —St —Mystate = —State ー {—Mid1 = —FI, —Mid2 = -F2; 引 se if ( *—Mid2 = = 0xfeff) —Ans = partial; if ( *—Mid2 = = Oxfffe) *—Mid2 ト * ー M 土 dl 十十 & 0xff; {*—Mid2 = *—Mid1 十十くく break; } } —Ans = 0 塒 { 十十—Mid2; —Mystate = } { —Ans = partia 嵭 else if ( *—Mid2 = = Oxfeff) —Ans = partial; if (*-Mid2 = = 0xfffe) *—Mid2 ト *—Mid1 十十くく return (-Ans); } —State = —Mystate ー —Mid1 = —FI, —Mid2 = —F2; if (—Ans = = partial ) break; } } —Ans = 0 新 { 十十一 M 土 d —Mystate = } {—Ans = partial; virtua ー resu ーし do—out ( —St& —State , if (—Mid1 = = ユ 1 Ⅱ—Mid2 = = ユ 2 ) {—Mid1 = —FI, —Mid2 = —F2; —TO *—F2, —TO *—L2, —TO * & —Mid2) const const —E *—FI , const —E *—LI, const —E * & —Midl, else if (*—Mid1 = = 0xfeff) return ( ok } *—Mid2 十十 = 0xff; *—Mid2 十十 = 0xfe; 十十—Mid1; {—State = 0 ・ else if ( *—Mid1 = = 0xfffe) return ( partia l) ー else if (—Mid2 十 1 ー return ( ok -L2) C MAGAZINE *—Mid2 十十 = 0xfe; * ー M 土 d2 十十 = 0xff; 十十一 id {—state = 1 ー 2000 7 {*—Mid2 十十 = (unsigned char)*—Mid1; *—Mid2 十十 = ( unsigned char ) ( * ー M 土 dl 十十 > > 8 return ( ok } (*-Mid2 十十 = (unsigned char) (*-Mid1 > > 8 *—Mid2 十十 = (unsigned char)*—Mid1 十 return ( ok } } virtual resu 比 do—unshift ( —St& —State , —TO *—F2, —TO *—L2, —TO * & —Mid2) const { return ( ok } virtual int do—length(—st& —State, const —TO *—FI, { return ( 0 ) virtual int do—encoding ( ) const —THROWO ( ) { return ( 2 ) virtual int do-max 」 ength ( ) const -THROWO ( ) {return (false); } virtual 引 do—always-noconv( ) const -THROWO( ) {return ((int)(-LI - -FI) / 2 ) const —TO *—LI, size—t —N2) const —THROWO( ) LiSt 少しまともな変換機のテスト用コード const char *fname = myfilen 引 write-file(wchar-t code = 0 ) い OC 引 e IOC = —ADDFAC(locale: :classic( ) , new Mycodecvt); wofstream myostr; myostr. imbue(loc); myostr. open(fname, ios—base: :binary); if ( !myostr. is-open( ) ) ”くく (int)i くく endl; {cerr くく write failed fO て i if ( !myostr. write(&i, 1 ) ) fo て (wchar—t i = i く 0xfff; 十十土 ) return ( fal se } くく (int)code くく endl; {cerr くく "failed for code if (code ! = 0 & & !myostr. write(&code, 1 ) ) return (false); } {cerr くく "can't write セ 0 ″くく fname くく endl; ADDFAC(locaIe: :classic( ) , new Mycodecvt); if ( !myistr. is-open( ) ) myistr. open(fname, ios—base: :binary); myistr. imbue(loc); wifstream myistr; い OC 引 e IOC = bO 引 read—fil e ( ) return (true); } return ( fal se } ”くく (int)i くく endl; return (write-file(arg) & & read-file( ) ) : *argv[l] 'b' ? 0xfeff : Oxfffe; {wchar-t arg = argc く 2 ? 0 int main ( int argc , char * *argv ) return ( true } return (false); } } {cerr “ "read failed for 土 if い myistr. read(&n, 1 ) Ⅱ n ! = i) {wchar-t n = fo て (wchar-t i = i く 0xfff; 十十 i ) return (false); } {cerr くく ncan't read from ”くく fname くく endl;

4. 月刊 C MAGAZINE 2000年7月号

ている挙動 ( メソッド ) とは分けて考えてい して , 通常は「メッセージ = メソッド」とし ます。すなわち , メッセージとメソッドは てしまい , 効率の悪い「メッセージ受信→ 別物という考えです。たとえば「キスをす 解釈→メソッド起動」をやりたい場合は「仮 る」というメッセージがあった場合 , これ 想関数」を使いなさい , ということにして を受け取る異性オプジェクトのメソッドは います。つまり C + + では , デフォルトは静 必ずしも固定していません。自分の奥さん 的 ( 固定 ) で , 動的なことをしたい場合は仮 メモリリークなどの予想しない資源の解 オプジェクトが「キス」メッセージを受けた 放し忘れ現象や , その結果で起きるバグに 想関数です。 悩まされます。 場合は問題ないのですが , 見知らぬ女性オ ところがこの方策は , いちいち動的なも [ 原因 ] プジェクトだと「殴る」メソッドを起動する のをするなら仮想関数と明示しないといけ かもしれません ( 「キス」メッセージを送る デストラクタを virtual にしないクラスを ないのがめんどうです。うつかり忘れて , 継承し , 仮想関数を利用すると弊害が起き オプジェクトがキムタクの場合は , すべて 意図しないメンバ関数を呼び出したり , メ る場合があることを知らないせいです。 の女性オプジェクトが「喜ぶ」メソッドを固 モリリークや資源の解放忘れを引き起こす [ 対策 / 予防 ] 定的に起動するのかもしれませんが : - ) ) 。 のが問題です。たとえば , List19 のような 勉強しなさい : - ) 。 このように受信オプジェクトによっては , ケースでは , 基底クラスである limal クラ [ 例外 ] ひとつのメッセージによって起動するメソ スのデストラクタがⅵれ ual でないため , the 継承させたりしないと決めているか , 継 ッドはひとっとはかぎらず複数である可能 limal でポイントしているオプジェクトが 承はさせるものの仮想関数をいっさい使わ 性があります。ところがこういう仕組みを Dog オプジェクトなのに , きちんと Dog ク ないと決めている場合は , かまわないかも 実現しようとすると , メッセージの受信と ラスのデストラクタが呼ばれません。画面 しれません。しかし , あとで状況が変わっ メソッドの起動をテープル化し , 動的に反 に出てくるメッセージは , て「しまった ! 」とならないように 応する仕組みを作らないといけませんし , * test start * [ 備考 ] メッセージ受信→解釈→メソッド起動によ Animal Start たいていのオプジェクト指向言語では , って , プログラムの実行速度を落とすハメ Dog_Start オプジェクトに対してある命令を送ること になります。 C + + は C 言語の「効率至上主義」 BOW! WOW! ( メッセージを送ること ) と , オプジェクト ( 効率のためなら自分の親兄弟を殺しても Animal End 知らないよ主義 ) を守るための苦肉の策と が外部の働きかけに反応するために用意し * test end * テストラクタがⅵ「 tu 引でない new ではなく ma Ⅱ oc を使った変な例 class Äれ土 m ( 0 ー ass TestCl ass { public,• char mHOge [ 128 Anima Ⅱ ) ( cout くく ' 土 m ー st セ第 public: Animal ( ) { co 乢 "Animal—End%n"; 〃← vi て tu 引でない ! void Set(const char* 土 n で ) : :strncpy(mHoge,inT,sizeof(mHoge)-I); わ virtual void Bark( ) 0 引 const char* Ge 日マ 0 土 d ) eons セ (xeturn mHoge; リ 0 ね“ Dog : P は 0 Animal ( st 土 0 void TestMain ( void ) public: Dog( ) ( co 此くくを Dog ー sta てセ第 } 咢 TestClass* theT -Dog( ) { cout くく "Dog—End*n"; void Bark( ) { cout くく OW ! wow!%nn; } す theT = static—cast<TestClass*>( t:maIloc(Sizeof(TestClass))) ー theT->Set()* test * ” CO くく theT->Get( ) くく end •:free(theT); デストラクタを v 減 ua にしない 深刻度☆☆☆ [ 症状 ] List List 20 AnimaI* Create—AnimaI ( 土 n ヒ inl) switch(inl){ case 1 : return new Dog; List delete ロでなく de 厄 te を使った static マ 0 土 d TestMain (void) TestClass* theT; theT ま new TestClass[81; de 厄 0 theT; static void TestMain(void) ~ AnimaI* theAnimal ま Create-AnimaI ( 1 theAnimaI->Bark( del ete theAnimal; 24 C MAGAZINE 2000 7

5. 月刊 C MAGAZINE 2000年7月号

[ 原因 ] にわかりますが , コンストラクタが 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

6. 月刊 C MAGAZINE 2000年7月号

MONTHLY HEADLINE Products Java 2 PIatform Standard Edition Ver. 1.3 がリリース Sun Microsystems より , Java 2 曰 a 廿 0 rm Standard Edition の最新版である Ve 「 . 1 .3 ( 以下 VI .3 ) の SDK および JRE ( Java Runtime Environment) がリリースされた。 Java TechnoIogy Home Page (http://ww w.javasoft.com/) からダウンロードでき VI .3 は , クライアントサイドのアプ リケーションやアプレットのパフォーマ ンス向上に主眼を置いている。ほかにも , 多くの新規機能が導入され , 既存機能に 対しても改良が加わった。その概要をレ ポートする。 パフォーマンスの向上 VI .3 では , 起動時間の短縮 , メモリ サイズの削減 , プログラム実行速度の向 上など , 全般的なパフォーマンスの改善 が図られている。 とくに , VM(Virtual Machine) に関し ては大きな変更がある。以前の VM と J げ ( Just Time コンパイラ ) の代替として , VI .3 では Java HotSpot Client VM を使用 する。この VM は , Java HotSpot Perfor mance Engine のサーババーションと同 様に , Java HotSpot 技術を利用してい るが , V. 1 .3 の VM ではプログラムの起動 時間の短縮を重視してお去クライアン ト環境に適している。 Java H0tSpot では , プログラムの性 能ポトルネックとなる部分 ( ホットスポ ット ) を検出して , その部分だけをネイ ティブコードにコンパイルし , インライ ン化などの手法で最適化する。ほかの部 ◎ URL http://www.javasoft.com/ 158 C MAGAZINE 2000 7 分はコンパイルせずにインタブリタで実 行するため , コンパイル時間を節約でき , その時間を最適化に充当できる。加えて , メモリ割り当ての高速化やカべージコレ クタの効率化 , スレッド同期の省メモリ 化とスピードアップが図られている。 VI .3 では , VM 以外にも , 起動時間の 短縮とメモリサイズの削減のため , 以下 ・ JAR のメモリ使用量削減 するため , 起動時間を短縮できる。 ったときに , 必要なクラスのみをロード していたが , V 1 .3 では実際に必要にな 以前は起動時に不要なクラスもロード ・ Swing のロード時間短縮 のような改良が施されている。 ポートしていたが , アプレット以外の Web 以前の Java Plug-in もキャッシュをサ トの起動時間を短縮する機能である。 おくことにより , 次回使用時にアプレッ プレットをローカルディスクに保存して キャッシング」は , ダウンロードしたア れた。そのなかのひとつ , 「アプレット 間の短縮に関するさまざまな改良が行わ アプレットに関しては , おもに起動時 アプレットの機能拡張 るため , 起動時間を短縮できる。 を , 実際に必要になったときにロードす は起動時にロードしていた一部の D LL DLL のサイズを縮小した。また , 以前 ・ DLL の縮小と遅延ロード 要なメモリ量を削減した。 を改善し , JAR ファイルのロード時に必 JAR ファイルのインデックス作成方法 ドキュメントとキャッシュを共用してい たため , 大きなアプレットではキャッシ ュがフラッシュされてしまうことがあっ た。 VI .3 では , Java 曰 ug - in のタグにお いて , キャッシュの動作を明示的に指定 できるようになった。新しいキャッシュ 機能を適用したアプレットは , ローカル ディスクに保存され , サーバ上で更新さ れたときにだけ再ダウンロードされる。 したがって , アプレットが更新されない かぎり , 常に高速起動が可能となる。 そのほか , アプレットサポートクラス のローカルファイル化 , 拡張機能の自動 インストール , JAR インテックスを利用 した JAR ファイルの分割 , といったダウ ンロード時間短縮 , 起動高速化のための 改良が行われた。 JNDI VI. 3 には , JNDl(Java Naming and Di rectorylnterface) が組み込まれた。 JNDI とは , さまざまなネームサービスやディ レクトリサービスを , Java アプリケー ションから , 統一的なインタフェイスで 扱うための枠組みである。 J N 引アーキテクチャは , API (Applic ation Programming lnterface) と SPI(S ervice ProviderInterface) で構成される。 Java アプリケーションは , API を用いて さまざまなネーム / ティレクトリサービ スにアクセスできる。一方の S 曰は , さ まざまなネーム / ディレクトリサービス を透過的にプラグインするためのインタ フェイスである。

7. 月刊 C MAGAZINE 2000年7月号

十五ロ ロ ば温度を表現するメンバ変数があったとし ば他人になるのは , プログラム開発ではあ んなこと知るか。 public にしたおまえらが りがちですから : - ) 。 悪い。責任をとって互換性を保つようにし て , これを直接読めばすぐに温度がわかっ ろ」のように , チームや社内で喧嘩のネタ て便利だと思っていたら , 仕様変更によっ [ 備考 ] C + + にかぎらない話ですが , 個人で細々 になりかねません。 てリアルタイムで温度が変化してしまうよ とプログラム開発しているのとチームでプ メンバ変数を public にした弊害に比べれ うな例が出た場合 , メンバ変数を直接読ん ば少しはマシかもしれませんが , いずれに ログラム開発するのでは , 条件が違いすぎ でいる箇所をすべて探し出し , そのすべて せよプログラムの修正がやりにくく , 修正 る場合があります。いうまでもなく個人で を ReadTemperature() のような関数に置き の結果 , ドッポにはまる危険のある点では , 行う場合は全責任が自分にかかるので予想 換えないといけないようなケースです。 のつかない使われ方は案外起こりにくいの さほど変わらないでしよう。 このようなケースでは最初から温度を取 ですが , チーム開発になるとどういうプロ 得するメンバ関数を用意しておけば , 仕様 内部の実装を表にさらす グラマがどういう工夫やズルをするかわか 変更があったとしても影響を受けるのはそ ( メンバ変数へのポインタを得る ったものではありません。その場合 , たい のメンバ関数だけであり , メンバ関数を利 メンバ関数を作る ) ていは「自分の都合のいいように」解釈され 用しているコードにはあまり影響が起こり 深刻度☆☆☆ トラブルを引き起こす可能性が大きいこと ません。 [ 症状 ] つまり , メンバ変数を直接触らせないの を認識すべきです。 予想しないバグが起こり , その原因が特 C + + のような複雑なプログラム言語では , は , 何も屈折した趣味でやっているのでは なるべく思考の負担を減らす方向にコーデ 定しにくくなります。 なく , コードの堅牢性や柔軟性を高めるた イングすべきです。それは , なるべく広範 [ 原因 ] めの工夫なのです。 内部実装を表にさらすことによる弊害を 囲に被害を及ばしそうな箇所を減らす , 具 pub ⅱ c にする必要のない わかっていない。というか , オプジェクト 体的には外部からアクセスされそうな箇所 メンバ関数を pub ⅱ c にする 指向の基本理念をわかっていないのです。 を減らす , なるべく副作用を起こしそうな 深刻度☆☆ [ 対策 / 予防 ] 要因を減らす , などです。 勉強しなさい : - ) 。 この禁じ手も , 先ほどの「メンバ変数を p [ 症状 ] [ 例外 ] ub ⅱ c にする」と同様 , 仕様変更があった場 想定されていない使い方をされることで , 個人で事情がわかっている場合や , 内部 合に困る要因になりえます。内部だけで プログラムの挙動がおかしくなったり , 使 の実装を前提条件であることを公にして使 細々と使うつもりだったメンバ関数を勝手 ってほしくない使われ方による事故や後始 に使われていた場合 , やはりどこでどう使 わせる場合はかまわないかもしれません。 末に追われてしまいます。 しかしその場合でも , なるべく規模の小さ われているのかを調べ , 対策を考えないと [ 原因 ] いクラスやコードに収めるべきでしよう。 いけません。どちらにせよ , あまり楽しい 実は個人で開発しているぶんには発症し 外部にポインタを教えるにしても , 外部か 仕事ではありません。たとえば「これは内 にくいのですが , チーム開発で発覚しやす らの書き換えによる弊害を防ぐため , const 部だけで , こっそり使うはずのものなので , い現象です。チーム開発の恐ろしさをわか ポインタで渡すようにしましよう。 使わないでください」と頼みにいっても「そ っていないことが根本の原因です。 [ 対策 / 予防 ] メンバ変数へのポインタを返す この現象は個人で開発している場合に cl ass A—String { は , あまり問題になりにくいものです ( た char* mBuffer;- 、〃文字列の格納バッファ だし , 時間が経過して , 過去の自分が他人 〃文字列の長さ int mLength; になってしまうようなケースは除きます ) 。 void lnit(void); public: チーム開発特有の落とし穴を認識し , それ A-String( A-String ( cons しÄ—String& A-String ( const char* を避けるよう配慮しましよう。 virtual -A-string( ) ( Clear( [ 例外 ] void CIear(void); 個人で事情がわかっている場合はかまわ char* GetText(void) const { return (NULL ー = mBuffer) ? ” : mBuffer; 〃↑これは問題あり ないかもしれませんが , やはり pub ⅱ c をす 土 n し GetLength(void) const ( return mLength; る範囲をなるべく狭くするという考えを持 ム日の自分は明日になれ つべきでしよう。フ List 特集 1 プログラミングの禁じ手 19

8. 月刊 C MAGAZINE 2000年7月号

たとえば , 定時的に処理をするためのサー ながら C + + でも被害甚大であり , むしろ C 言 ビスを提供する抽象クラスとして LPeriodic [ 例外 ] 語以上に注意しないといけません。 メンバをたくさんそろえることで汎用的 al , オプジェクト同士の交信をするための なし。 な利用を増す , あるいは豪華な仕様である [ 備考 ] LBroadcaster/LListener など , いくつかの ことが売り物になる , という勘違いがあり 規模に関する被害は甚大であることは禁 軽いクラスから成り立ち , それらのクラス ます ( とくに商用クラスライプラリでよく じ手の C 言語編でも説明しましたが , 残念 には共通の基底クラスがありません ( した がって菱形継承の問題も起きません ) 。 複数のメンバ関数を操らせる うすることで継承レベルをきわめて浅くで # ー ude く ostream> きます。 using namespace std ー 商用クラスライプラリは機能の充実さを class CFi leCopy { typedef bO 引 ( *UserCancel Func ) ( const CFil ecopy& , void* 売り物にしようとするあまり , ついつい豪 typedef VOid ( *DirDownFunc ) ( const CFi ー ecopy& , const char* , void* typedef VOid ( *DirUpPunc) (const CFi leCopy&'const char*'void* 華絢爛な肥満体のクラスや深い継承レベル typedef VOid ( *Fi leCopyStartFunc ) ( const CFi ー ecopy& , const char* , void* typedef VOid ( *FileCopyEndFunc) (const CFileCopy&,const char*'V0id*); になりがちですが , PowerPIant は多重継承 public: CFiIeCopy( を取り入れることで痩せたクラス , 浅い継 virtual ¯CFiI ecopy ( 承レベルにするという , それまでの商用ク SetSourceDirName(const char*); / / コヒ。ー元のディレクトリ名をセットする VOid 〃コピー先のディレクトリ名をセットする SetDestDirName ( const char* VOid ラスライプラリとは違う考えで出てきたも SetCreateDir(booI); / / コピー先のディレクトリが存在しないときに作成する VOid 〃かの指定 のです。そのため学習しやすい体系になっ SetUserCancelFunc(UserCancelFunc); / / コピー中のユーザキャンセルの VOid / / 入力受け付けコールバック関数のセット ています ( それでも , 商用クラスライプラ SetUCFuncMisc(void*); / / ユーザキャンセルの入力受け付けコールバックの VOid / / 第 2 引き数のセット リ特有の機能充実主義のせいか , 今度は縦 SetDirDownFunc(DirDownFunc); / / ディレクトリ降下時に呼び出す VOid 〃コールバック関数のセット 軸ではなく , 横軸に広い体系になってしま SetDDFuncMisc(void*); / / ディレクトリ降下時のコールバックの第 3 引き数の VOid / / セット い , それが原因で学習しにくい点も否定で void SetDirUpFunc(DirUpFunc); / / ディレクトリ上昇時に呼び出す 〃コールバック関数のセット void SetDUFuncMisc(void*); / / ディレクトリ上昇時のコールバックの第 3 引き数の きません ) 。 / / セット void SetFiIeCopyStartFunc(FiIeCopyStartFunc); / / 1 つのファイルコピー開始時 / / に呼び出すコールバック関数のセット クラスのメンバ変数あるいは void SetFCStartFuncMisc(void*); / / 1 つのファイルコピー開始時のコールバック / / の第 3 引き数のセット メンバ関数が多い void SetFiIeCopyEndFunc(FiIecopyEndFunc); / / 1 つのファイルコピー終了時に呼 / / び出すコールバック関数のセット 深刻度☆☆☆ void SetFCEndFuncMisc(void*); / / 1 つのファイルコピー終了時のコールバックの / / 第 3 引き数のセット void ExecCopy(void); / / コピーする [ 症状 ] int GetErrorCode(void); / / 工ラーコードをえる 自分の使いたいメンバ関数を探すのに苦 労します。メンバ変数のからみが多いため 検証すべきメンバ関数が多くなり , プログ ラムがなかなか完成しにくくなります。ま たバグを発見しても直しにくくなります。 [ 原因 ] メンバが多いことによる弊害を認識して いません。複数のクラスに分割する方法を 知らないか , めんどう , メンバがたくさん あると豪華な仕様であり自分が賢いことを 証明できると勘違いしています。 [ 対策 / 予防 ] 継承を使って分割できないかを検討しま しよう。たとえばよく使う基本機能だけの クラスを先に作り , 派生クラスを作ってそ ちらに拡張機能を実装するのも手です。ま た , 複数のクラスに分離できるのなら , そ ちらを検討すべきでしよう。 1 static VOid TestMain(void) CFil eCopy theFC; theFC. SetSourceDirName( . SetDestDirName ( "YYY" theFC theFC. SetCreateDir ( true . SetUserCance ー Func ( MyUserCance ー Func theFC . SetUCFuncMisc ( NULL theFC . SetDirDownFunc ( MyDirDownFunc theFC . SetDDFuncMisc ( NULL theFC . SetDirUpFunc(MyDirUpFunc); theFC theFC. SetDUFuncMisc ( NULL . SetFi leCopyStartFunc(MyFi leCopyStartFunc); theFC . SetFCStartFuncMisc ( NULL theFC . SetFi ー eCopyEndFunc ( MyFi ー eCopyEndFunc theFC SetFCEndFuncMisc ( NULL theFC. if( 0 ! = theFC. GetErrorCode( ) ) { }else{ theFC. ExecCopy( if(0 = = theFC. GetErrorCode( ) ) { プログラミングの禁じ手 特集 1 15

9. 月刊 C MAGAZINE 2000年7月号

Java プロクラミングリファレンス 詳説 JDK 解は新ロ か , fo 「文における「初期化部分」およ び「更新部分」のいずれかがそれに該 当する これらの検査のどれかに適合しない場合 , コンパイルエラーとなる。 以上の検査をすべて通過してコンパイル 時の作業は完了であり , コンパイラは , 各 メソッド呼び出しについて Fig. 7 に示す内容 の情報をクラスファイルに書き記す。以下 で説明するように , 実行時にこの情報を参 照して , 実際に呼び出すメソッドを最終的 に決定するのである。 メソッド呼びし ~ 実行時の作業 コンパイル時にかなりのチェック作業を 終了しているとはいえ , Java におけるメソ ッド呼び出しでは , 実行時にもかなりの作 業工数が必要になる。 Fig. 1- ( b ) にも示した ように , その作業は五つのステップから 構成される。 呼び出し対象オブジェクトの特定 条件から妥当なものであるかどうかを検査 Fig. 7 各メソッド呼び出しに関連して記される情報 実行時にまず行うのは , どのオプジェク することである。 1 . メソッドの名前 トを通してメソッド呼び出しを行うのか ( あ この検査は以下の三つに分けられる。 2. メソッドのコンバイル時の宣言が行われた 型 ( クラス 0 「インタフェイス ) (l)static メソッドの呼び出しのみが可能 るいは , そのようなオプジェクトはないの 3. 仮引数の個数とそれぞれの型 か ) を定める作業 , すなわち対象オプジェク なコード部分から呼び出しているメソ 4. コンバイル時に宣言された返り型 トの決定である。簡単にいえば , static メソ ッドが , 実際に s ね tic メソッドである 5. 以下で決定される起動モード ッドを呼び出す場合 , 対象オプジェクトは かの検査。ここで , static メソッドの 5-1 . コンバイル時の宣言に static 修飾子が指 定されていれば , 起動モードは static ない。そうでない場合には , 何らかの形で 呼び出しのみが可能なコードは , sta 5-2. さもなくて , コンパイル時の宣言に 対象オプジェクトを特定する。以下 , 該当 tic メソッドの本体 , static 初期化子の p 「ⅳ ate 修飾子が指定されていれば , 起 メソッドは s ねⅱ c でないことを前提にする。 内側 , static 変数の初期化子部分など 動モードは nonvirtual メソッド呼び出しのソース上の記述が単 である 5-3. さもなくて , メソッド起動の名前指定部 分「 supe 「 . 識別子」の形であれば , 起動モ なる識別子からなる場合 , たとえば o ( x , ( 2 ) メソッド名に「型名 . 識別子」という形 ードは supe 「 y ) 」のような形式であれば , これは実は「 this. 式を用いて呼び出しているメソッド 5-4. さもなくて , コンバイル時の宣言がイン ( x , y ) 」の省略形であると解釈されるから , が static メソッドであるかの検査 タフェイス型で行われていれば , 起動モ 対象オプジェクトは this の値である。 ( 3 ) 呼び出しているメソッドの返り型が ードは interface 5-5. さもなければ , 起動モードは virtual 呼び出し対象オプジェクトの指定部分に void である場合には , それを呼び出 式が用いられている場合 , その式を評価し , すことが許される文脈からの呼び出 その結果得られるオプジェクトを対象オプ しであるかの検査。ここで , 返り型 をする対象のメソッド名 , そのメソッドを ジェクトとする。なお , もしも式の評価結 が void のメソッドを呼び出すことが 呼び出す型 , およびメソッドシグネチャま 果が nu Ⅱであったとしても , この時点では 許されるのは , トップレベルの式 , でが決定された。コンパイル時に行う最後 まだ工ラーにはしない。ただし , 途中でこ すなわち , 「式」の値を利用しないよ の作業は , このようにしてシグネチャまで の評価が中断終了した場合には , その時点 うな文脈である。具体的には , 式文 が特定されたメソッドの呼び出しが , 文脈 メソッド呼び出し変換の例 / * List1.java * / キメゾッド呼び出し変換の例 * 定数 10 はあぐまで土北であって , に e には変換されない 0 lass F00 ( public void zot(double も double y). { System.out.println("Foo. zot((double)" 十茎十” ( d0 面尸十 y 十つ” ) 0 lass Bar extends 00 { p 面は 0 void ZO 日土 n sh0 て 0 Y) ( 号 ystem ・ ou し・店土 nt ⅲ ( " B .20t ( ( 土 n し尸十区十ツ , ( s れ or し戸十 Y 十、尸嵭 class Baz extends お a て ( public void zot(byte a でⅵ ( system. 0uこ . println("Baz. Z0t((取しe广十十。 % 往 h 矼尸十十 ? 尸リ class s セ 1 ー ( public static main(string[ 1 args) イ Baz myBaz れ e 曾 Baz( 嶹 マ / どのメソッドが呼び出されるか ? myBaz.zot(10,- ' 記 / / char → sho てしへ、強制的に型変換することは可能 叫 Baz 、 40t ( 10 , ( 曲 0 てむ ) ' / / この初期化でば、加が代入変換で by に e に変換されることに注意 byte わ = 1 の / / こんどはどのメソッドが呼び出されるか ? nyBaz. zot(), ' 記 ) ー Java プログラミングリファレンス詳説 JDK 解体新書 115