void - みる会図書館


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

1. 月刊 C MAGAZINE 2000年9月号

真紀俊男の ローテク講座 ひとつのインスタンスのみ保証 ひとつのインスタンスのみ発生のつもり class SingleCIass ( public: SingIeCIass( ) { -SingleCIass( ) ( void N0thing(void) const { static SingIeCIass* Ob static VOid Create(void) { if(NULL = = Obj ) { Obj = new SingIeClass; class SingIetonCIass { static SingletonClass* SObj; protected : SingIetonCIass( ) { public: ¯SingletonClass( ) { void Nothing(void) const { static SingletonClass* Obj(void) { if(NULL = = sObj ) { sObj = new SingletonCIass; return sObj; static void DeIete(void) { delete Ob Obj = NULL; SingIetonCIass* SingletonClass: :sObj = NULL ・ int main( ) SingIeCIass* SingIeCIass: :Obj = NULL; int main ( ) SingIetonCIass* theObj = SingIetonCIass: :Obj( theObj → Nothing( delete theObj; / / 唯一のインスタンスの解放 / / ( ただし、これは感心できない。禁じ手であろう ) return 0 ー ::create(); 〃唯一のインスタンスの発生 SingIeCIass SingIeCIass: :Obj->N0thing( SingIeCIass::Delete(); 〃唯一のインスタンスの消減 return 0 ー 自動解放をする #include く memorp int main( ) うところの SingIeton パターンと呼ばれてい 解できない。 るものである。こうすれば唯一のインスタ ンスしか存在しえなくなり便利である。コ [ 1 ] 間違ったアドバイスはかえって害にな ンストラクタが public メンバでないため わざと SingIetonClass な自動変数を作った る り , new でインスタンスを発生することは アドバイスが間違っていなくても , 受け 取るほうが誤解することは , よくある話で できない。 ある。どう受け取ったかをあとで確認して ただし , こうしても唯一のインスタンス おく必要はあるだろう。 の解放を明示的にしないといけないのが何 となくめんどうであるし , 解放してからメ [ 4 ] 問題は解決する [ 2 ] リンク段階でエラーになる ンバ関数を呼んでクラッシュする危険を完 ところが 全に防ぐことはできない。そこで別の仕掛 ただし , これも落とし穴があり , 誰かが GLOBAL int glnteger = 1 ; けを併用して自動解放する工夫をすると , 同じ名前のグローバル変数を同じソース内 みたいなことをされると , うまくいかない より安全であろう (List11)0 に定義してしまうと , そちらのほうにアク こでは auto ー ptr を利用している。 auto— セスしてしまう。もちろん , これは同名の ptr は標準ライプラリとして , たいていの処 別人であるからうまくいくはずがないのだ [ 5 ] 資源確保の面と速度の面で若干有利に 理系に付いてくるもので , なかなか便利な 仕組みなのだが , どういうわけか「食わず ただし , 確保されるタイミングが予測し 嫌い」で敬遠しているプログラマもいる。 [ 3 ] あとから解析・調査するのが楽になる にくくなる弊害もある。あらかじめ , しかも , わざわざ自分で自動解放のプログ いい換えれば , あとからの解析・調査は TestObj ( ) ; ラムを作り , そのバグでハマり込んで , し 楽だが , プログラムを組むほうは楽ではな という一見意味のない行を冒頭で実行し なくていい苦労をしているのが筆者には理 いということである ( 笑 ) 。 て逃げる手もある。 150 C MAGAZINE 2000 9 [ 注 ] auto—ptr く Sing ー etonC ー ass> theObj(SingIetonCIass: :Obj ( ) theObj->Nothing( return 0 ー

2. 月刊 C MAGAZINE 2000年9月号

ist テンプレートのコード 1 とスコードの解析 今回テンプレートに利用したサンプルは , 実行結果からもわかるようにウインドウを 使っていません。そのため , 生成されるコ ードも非常にシンプルです ( List1 ) 。 こで定義された関数は次の三つです。 void InitApplication ( ) void CleanupApplication ( ) void He110Pane::Draw() 関数名からもわかるように , これらはコ ードの初期化 , 終了処理 , そしてメイン動 作をそれぞれ担当しています。 メイン動作の関数では , 表示する文字列 を定義し (List 1- ① ) , 表示する位置を決定し (List 1- ② ) , 実際に表示する (List 1- ③ ) とい う流れになっています。 pt で定義されてい る表示位置や , message 文字列を変更して 実際の動きを試してみましよう。 ウインドウを持った PowerP 狙 s の選択画面では「 hello 」ではなく その一部に円が描画されているのがわかり プログラミング 今回は「 circle 」を選択します。 ます (Fig. 5 ) 。 次に , ウインドウを持った MORE ソフト プロジェクトが作成できたら , これも先 ソースコードの解析 を作ってみます。先ほどと同様に新規プロ ほどと同じ手順で実行してみましよう。ア ジェクトからテンプレートを利用しますが プリケーション内にウインドウが表示され , ここで生成されるソースコードは , 先ほ List 2 tst ウインドウ基本部分 PLCommon. h" #include 卩 #include "PLUtilities. h" ヨ #include "PLAppearanceUtils. h" ヨ *include "PLThemeManager. h" "PLThemeView. h" #include #include "PLThemeGroupBox. h" #include "PLGraphiCSPOrt. h" #include "PLClassRegistry. h" inc ー ude "PLStream. h" #include "PLDocumentWindowAppearance. h" #include ” PLAppearanceFactory. h" #include "PLPane. h" #include "Circle. h" extern PLDocumentWindowAppearance ー / /prototypes VOid InitApp ー ication ( PLGraphicsPort* , PLEventLOOP* eventLOOP void CleanupApplication( ); class TestAPP : public PLEventHandler public: #if MW—COMPONENT *endif ョ PLComon. h" ヨ #include ヨ #include ” he Ⅱ 0. #include "PLDrawContext. h" "PLFont. わ” #include #include "PLView. h" #include ” Windo 等 . h ” #include "PLString. h" #include "PLAppearanceFactory. const PLIdentifier kHelloPaneID = FOUR—CHAR—CODE( , 'e' , ' ド , ゆ ' ) ; void InitAppIication(PLGraphicsPort* ても PLEventL00P* /*eventL00P*/) Hellopane* pane = new Hellopane(kNullPoint, →ætWidth( ) , port->GetHeight( ) ) port → GetR00tView( )->AddSubPane(pane); 卩 void CIeanupAppIication( ) HelloPane: :HelloPane(const PLPoint& location, const PLPoint& size ) PLPane い ocation , size , kHe Ⅱ oPaneID) VOid HelloPane: :Draw(PLDrawContext& context) PLRect て ( 0 , 0 , msize. h, msize. v); context. 、 aseRect ( て ); char “ age けをようこそ 20 能て pa れ目 0 d へ物 00n 日 0 PL?ont* font ℃い GetFon 日 PLPoint pt ー bt 五 (mSize. h 、 font->StringWidth(message)) 子第 . v ヨ 2i2 v , font->GetAscent( リノ 00 北し以 a t て i 朝アい土 ( s “を一ね % 玉れ加 R 地 h い ①②③ PLView* rootview = port—>GetRootView( InitDefaultTheme(rootView); app = NEW TestApp(rootView, eventL00P); eventLoop->SetDefau ー tTarget ( app eventLoop->SetTarget ( app TestApp: :TestApp(PLView* PLEventLoop* eventLoop) ・ mRootView(rootView), mEventLoop ( eventLoop) PLP0int size ( 300 , 150 リ PLPOint c ( 0 , 0 PLString str ( "Circ ー e test", true , kLeftToRight PLWindow *mwind = new PLWindow い oc ー size, str, kDocumentWindow, this); if (mWind) kC loseBox ー kZoomBox ー kMinimizeBox mRootView->AddSubPane(mWind); mComp:ment = new Ci て 0 厄 ( (mcomponent) PLPoint c ( 50 , 50 PLP0int csize(32,32); mComponent—>SetSize ( csize mComponent->SetLocation い OC 潺 TestAPP( PLWindow *mWind; Circle *mComponent; TestApp ( PLView* rootView, PLEventLoop* eventLoop virtual ¯TestApp( PLView* mRootView; PLEventLoop* mEventL00P' #if STREAM-OUT PLStream* #endif mWindowStream ー void CleanupApplication( ) delete app; TestApp* app; void InitApp は ca に土 on ( PLGraphiCSPOrt* , PLEventLoop* ) 64 C MAGAZINE 2 0 9

3. 月刊 C MAGAZINE 2000年9月号

があります。 test. cpp circle. cpp どの he Ⅱ 0 プログラムよりもだいぶ複雑にな っています。それぞれのコードは次の役割 #include "Circle. h" #include "PLPoint.h" #include し pane. ド ウインドウ基本部分 ( List 2 ) 実際に円を描く部分 ( List3 ) このため , ウインドウを持ったアプリケ ーションの開発を行う場合はこの PowerP ts の circle テンプレートを使い , circle. cpp と 目的の処理を行うコードを入れ替えれば OK です。 、 Poøer Parts Fig. 5 円の描画 #include "PLEventHandler. h" #include "PLDrawContext. h" 円 eom 虻 . h" 円を描く #include ウインドウを表示する プログラム nSe ⅲ ng ] メニューを選択すると確認できま の識別名は , [Zaurus] → [Zaurus AppIicatio を見てみましよう。このアプリケーション さて , こでアプリケーションの識別名 アプリケーション詳細設定 グラムができあがるはずです。 行してみましよう。単純なウインドウブロ この状態でコンパイル , メイクを行い実 変更します。 のウインドウタイトル部分を List 4 のように ロジェクトから circle. cpp を削除し Test. cpp ロジェクトを作成します。できあがったプ まず , circle テンプレートを使って新規プ ムを作ってみましよう。 て , ウインドウを表示するだけのプログラ circle テンプレートを利用した応用とし 特集 2 PDA プロクラミン % す。このダイアログボックスでアプリケー ション名や識別名 , アプリケーションタイ プを設定することができます ( Fig. 6 , Tab le 2 ) 。 PowerParts による RAD 開発手法 それでは , いよいよ CWfor Zaurus の真骨 頂 , PowerParts を使った RAD の開発手法を 見ていきましよう。 Zaurus にかぎらず , 通常新しいアプリケ ーションを作成する場合は当然コードを手 作業で入力しなければなりません。そのな かには , ボタンの大きさなどといったオプ ジェクト情報も含まれます。開発ツールに リソースエデイタがあればうまく補助して くれますが , そのオプジェクトの初期化ル ーチンなどのコードはやはり手作業での入 力です。 RAD では GUI を前提としているため , れらのオプジェクトに関するルーチンや情 ヨ ョ ヨ 1 1 1 ヨ 丿 1 1 1 #if STREAM-IN Ⅱ STREAM. -0い #include "PLStream. h" #if MW—COMPONENT long g—RefCount = #endif Circle: :Circle( ) #if MW—COMPONENT Circle::-Circle() #if MW-COMPONENT g—RefCount—— g—RefCount 十十一 ー ocation = GetLocation( VOid Circle: :Draw(PLDrawContext& context) 2 い聞 e : : 此 a 響 ( u 〆 at 駅 gn void C 辷 0 厄 : : n 引 e 此 a 響 ( 00n 北 PLRegion* リ図北 gn ) PLPOint context. PaintE Ⅱ ipse 仕 ect rect. 2にt0m = 【・ 0 セ . て igh し一 て ect. て igh に = て・ ct. に ton ー if ( て ec し g れセ〉て ec し社 om ) EraseRect ( てに PI 派に rect(), 0 , size. h, si 記 . v PLPoint size = GetSize( 引 C 辷 0 厄 : :HandleyouseEvent(const ~ し po 報 p, PLEventID e e れ切 return (PLpane: : 当 ouseEve 北 ( p , event, button)); uouseButton button) PLPane: :Mousemvn(), bu セセ on void Circle: :Yousemwn(const PLPoint& 店 uousemtton button) List 3 void Circl e : ・ PLPane: void Circle: PLPane: . MouseUp(const PLPoint& p, EMouseButton button) :MouseLeave(p); :MouseLeave(const PLPOint& p) :MouseEnter(p); void Circle: :MouseEnter(const PLPoint& p) PLPane: :MouseUp(), button); extern 物 C 第 D ー COMP 風打 - P ( ) CO 当製風打 -. FI Ci て引 e. h ' ) COMPONENT—FILE("Circle. cpp") 風 2- c PON 風打 - まゞ望打 s ( Ci て cle ) *. -COMPONENT-EVENTS(Circle) 望ー CO 当ー風打 - ROP 駅 TI ( Ci て 0 厄 ) *. COMPONENT_PROPERTIES(Circle) DEFINE—DmVED—COMPONENT("Circle", Circle, PLPane, #ifdef MW—COMPONENT PLPane: :MouseMove(newpoint); void Circle: :MouseMove(const PLPoint& newpoint) Circle: ・反3eg30てiPt0て = REGISTER—COMPONENT( registry , Circ 厄 ー dec 地 0 ( d Ⅱ北 ) void PLU i 北 e て ( 期 co nen 駅 i 社 i ) —declspec(dllexxxt) PLCanUnloadDLL() Circle); て e にリて n !g—RefCount; ) //extern を c ・ #endif / / - c ON 特集 2 携帯端末を使いこなす PDA プログラミングのコツ 65

4. 月刊 C MAGAZINE 2000年9月号

BorIand 0 + + B ⅶ d ビジュ刋レ開発環境のアーキテクチャ す。 List4 は , プロバティを使ってデータメ ンバの FCelcius を摂氏 (Celcius) として直接 読み書きしたり , メンバ関数を経由して華 氏 (Fahren) として読み書きできるように した例です。 TDegree のオプジェクトへの ポインタ degree ( オプジェクトを new で生 成することに注意 ) に対して , degree->Fah ren = 50 ; と記述すれば , これは SetFahren ( 50 ) という呼び出しに置き換えられて FCe lcius メンノヾに 10 が代入されることになり ます。 C + + を学習した人は , 「データメンバは 非公開にして外部からのアクセスを禁止す ること。必要ならばアクセスするためのメ ンバ関数を用意すること」ということを教 えられているかもしれません。これは , オ プジェクトの書き換えを防ぐという安全な プログラミングのために重要なことです。 プロバティは , データメンバのように扱え ますが , 必要に応じてメンバ関数を割り当 てることができるため , C + + の安全性を損 published ないません。 void ——fastcall TForm1: :Button1Click(TObject *Sender) 実行時のコンポーネントの生成 List 2 では , private や public などの通常の C + + のアクセス制御に加えて , __published という新しい予約語が使われています。 —published も C + + のビジュアル開発を支 える重要な拡張です。 -published は , C + + 言語の構文として は pub ⅱ c と同じアクセス制御を持ちます。し かし , C + + BuiIder のコンパイラは—_publis hed 部のメンバに対して , 拡張された実行 時型情報を生成します。 _-published 部に 宣言されたメンバは , そのメンバの名前を 実行時に参照できるようになります。これ により , 開発環境が実行時パッケージに埋 め込まれたコンポーネントクラスの情報か ら , プロバティをオプジェクトインスペク タに表示できるのです。 すべてのコンポーネントクラスの基本ク ラスである TObject は , この実行時型情報 を利用できるメンバを持っています。たと えば , MethodAddress というメンバ関数に -published 部のメンバ関数名を渡すと , その関数のアドレスを返します。付録 CD- ROM には , ーー published 部の実行時型情報 を利用した簡単なプログラムが収録されて います。 ——published 部の型情報は , IncludeWcl にあるヘッダファイル TypInfo. hpp に定義 されている関数で扱うことができます。 C + + Builder ではフォームもコンポーネント クラスの一種だということを思い出してく ださい。 List 5 は , TForm クラスの __publi shed に定義されている名前の一覧をリス トボックスに追加します。 イベント List 5 を実行して表示されるフォームの ——published 名一覧は , プロバティ名に続 いてイベント名 ()n ・・ ・・という名前 ) が表 示されることがわかります。 C + + Builder で は , イベントとはプロバティなのです ( イ べントとしてのプロバティは , 通常データ メンバを指している ) 。 プロバティとイベントを区別しているの は , その「型」です。整数型や文字列型な ど通常の型を持つプロバティは , オプジェ クトインスペクタの [ プロバティ ] ページ に表示されますが , イベントは「クロージ ヤ」という型を持っています。クロージャ は , メンバ関数へのポインタに似た型です 通常のメンバ関数へのポインタが任意のオ プジェクトに適用できるのに対して , クロ ージャはメンバ関数のポインタを得る際に オプジェクトを特定します。 static int Num = 0 ー TButton *btn = new TButton(this); btn—>Parent ま this ー Num 十十一 btn->Caption = Dynamic 第十 IntTOStr(Num); btn->SetBounds ( Button1->Left, 16 十 30 *Num , Button1->Width , 25 btn->OnC lick = Button1C は c プロバティの例 class PACKAGE TDegree : public TComponent private : double FCelcius; void —fastcall SetFahren(double V) ( FCeIcius = (V - 32 ) * 5 / % } double —fastcall GetFahren(void) { return 32 十 9/5*FCeIcius; ) —published: —property double Celcius = { read=FCelcius, write=FCelcius —property double Fahren = { read=GetFahren, write=SetFahren TForm の __published 名一覧 void —fastcall TForm1: :Button1Click(TObject *Sender) PTypeInf0 InfO = —typeinf0(TForm); 土 n セ count GetTypeData ( InfO ) —>propCount ー { ShowMessage(Msg 十 ”十 Name); } void func(String Name) TFoo(string s) : Msg(S) { ) public: string Msg; private: class で F00 { メンバ関数へのポインタとクロージャ de ー ete List ー ListBox1—>Items->Add(List[i ト >Name); fo て (int 土 = 土く count; i 十十 ) GetPropInfos( InfO, List); PPropLiSt List = new PPropInfO ccountl; 特別記事 BorIand C + + BuiIder ビジュアル開発環境のアーキテクチャ cptr( Maya" (). *fptr) ( "watanabe" void (—closure *cptr) (string Name) = &a. func; void (TFoo: :*fptr) (String Name) = &TFoo: :func; 00 a ( e Ⅱ 0 つ , b("welcome"); void —fastcall TForm1: :Button1Click(TObject *Sender) 73

5. 月刊 C MAGAZINE 2000年9月号

Java プロクラミングリファレンス 詳説 JDK 解体新ロ 間接的な構造 ( Fig. 4 ) を持つ多次元配列は , とつ短くなる。これを表現するためには要 高い柔軟性を持っていて , さまざまな用途 素数が順に減少する三角行列が便利であ において効率のよい利用が可能である。 る。これらの例でわかるように , Java 流の . 非矩形の配列 Fig. 10 List 5 のサンプルラン akida:¯/cmaga/javaref$ java List5 Th Qui nFox mpsOverT azy Quic kB rownFoxJ IS ckBrow * 非矩形の配列 class List5 { static string quick = "TheQuickBrownFoxJumpsOverTheLazyDog" static int ix 0 ・ DogThe char n() ( if ()x > = quick. length( ) ) ix = return quick. charAt ( ix 十十 void show( ) ( f0 て ( 土 n し土 = i く x.length; 十十 i ) { f0 て (int j = の j く x[i].length; 十十 j ) system ・ out.print(x[il[ ] System. out. println( ) デ Fig. 11 List 6 のサンプルラン akida:¯/cmaga/javaref$ java L 土 s し 6 3 0 1 16 81 256 625 1296 2401 1 15 65 175 369 671 1105 14 50 110 194 302 434 36 60 84 108 132 24 24 24 24 0 public static void main(string[ ] arg) { List5 myl = new List5( List5 my2 = new List5( myl. show( my2. show( List 6 Java における配列利用の例 ( 三角行列を利用した階差数列表示 ) * Java における配列利用の例 * 三角行列を利用した階差数列表示 public class List6 { int[l int [ Ⅱ ] List6(int n) { switch (n) ( case 0 : X = new int[l { 1 , 1 , 2 , 3 , 8 , 13 , 21 , 34 , break; case 1 : x = new int[) { 1 , 4 , 9 , 16 , 25 , 36 , 49 , 81 , 100 , 64 , break; case 2 : { 1 , 2 , 4 , 8 , 16 , 32 , 64 , 128 , 256 , 512 , 1024 , 2048 , x = new int[l break; case 3 : x=newint[] ( 0 , 1 , 16 , 81 , 256 , 625 , 1296 , 2401 , breaki y = new int[x.length)tl; void calc() { fo て (int i = i く x. length; 十十 i ) { y[il = new int[x.length - il; for (int j = の j く y[i] . length; 十 + j ) ⅵ i Ⅱ j ] = y[i - 1 Ⅱ + 1 ] - yti - 1 Ⅱ j void show( ) { for (int 土 = i く y. length; 十十土 ) { for (int j = j く y[il.length; 十十 j ) system. out.print(yti][ j) 十” system. out. println( 〃メイン / / コマンド行引数で指定した数列に対する階差数列を表示する public static void main(string[ ] arg) { int n = arg ・ length > 0 ? lnteger ・ parseInt(arg[Ol ) List6 my = れ e 等 List6(n); 叫 . ca に ( my. show( 詳 DK 解体新書 Java プログラミングリファレンス 121

6. 月刊 C MAGAZINE 2000年9月号

List 1 List 前回の名前付きカウンタ 1 return *this ー * 名前付きカウンタ ( 修正版 ) 響 inc ー ude く iostream> #inc ー ude く cstring> using namespace Std ー c ー ass named—counter { char* name ・ int value; named-counter ( ) { delete[ ] name; named—counter& clear(int n value = n; return *this ー named—counte て & increment( ) { 十十 value; て eturn *this ー public: 〃コンストラクタ named—counter(const char* n=n name = new char[strlen(n) 十 1嵭 strcpy(name,n); clear(v); / / コピーコンストラクタ named—counter ( const named—counter& c ) name = new char[strlen(). name) 十 1嵭 strcpy(name, c. name); value = C. value; 〃代入演算子 named—counter& operator= ( const named—counter& 0 ) { deIete[J name;// 旧い領域は棄てて name = new char[strlen(). name) 十 ll;// 新たに確保 strcpy(name, c. name); value = C. value; int get—value( ) const { return va ー ue ー , int v = 0 ) { const char* get—name ( ) { return ー * お試し int main ( ) { named—counter c 0 ( "my—counter") 0000d ー 0000 に er c1 ( "your—counteri) ー cl = c0; cout くく cl. get—name( ) くく くく cl. get—value( ) くく endl; return 0 ー class named counter : public counter { 追加分・・・ のように , クラス名のあとに : と public' そして基となるクラス名を書き , 追加分を Ⅱでくるみます。 class D : public B { ・ であるとき , ・「 B は D の基底 ( base ) クラス」 ・「 D は B の派生 (derived) クラス」 ・「 B から D を導出する」 などと表現します。 named—counter で新たに追加するのは , List 継承の基となるクラス ( counte 「 . h ) List 継承の基となるクラス (counte 「 . cpp) * counter. h #ifndef --COUNTER—H— #define ——COUNTER-H— class counter { int value; public: counter(int v = 0 void increment ( ); -void set—value(int V); int get—value( ) const; * counter. cpp #include "counter. h" counter : : counter ( int V ) { set—value(v) ・ void coun し e て : :set—value(int v) value = v; int counter : : get—va ー ue ( ) const return value ・ #endif void counter: :increment( ) { set—value(get—value( ) 十 1 List 4 List 4 counter を継承して named_counte 「を作る (named_counte 「 . h) * named—counter. h を導出する * counter から named—counter #ifndef —NAMED-COUNTER—H— #define ——NAMED-COUNTER—H— # 土 nc lude "counter. h" class named—counter : public counter char* name; / / コンストラクタ named—counter(const char* n, int v = 0 / / デストラクタ named-counter ( / / コピーコンストラクタ / コピー演算子 named—counter ( const named—counter& c named—counter& operator= ( const named—counter& c / / 名前の設定と取得 void set—name ( const char* れ const char* get—name( ) const; #endif public: 90 C MAGAZINE 2000 9

7. 月刊 C MAGAZINE 2000年9月号

なかったお前が悪いのだ。こんなもの , バ グではない ! 」というバグ否認の回答を出 しませんでした。なぜか ? 私はこの件に JDK 1.2 で java. lang パッケージに導入された 妙なクラス , ThreadLocal が関係している のではないかと邪推いたす次第であります。 まず , 浅く簡単に今回の " バグ認定 " の根 拠を推定してみましよう。第一に考えられ るのは , vola ⅱ le 修飾子を使うべき場合に関 する正しい精密な知識が Java 言語使用者が もつ基本知識の中には一般的にないだろう ということです。私が提出したバグレポー トに参考資料として添付した C のプログラ ムは , 構造的には Emp Ⅳ .java とほぼ同じで すが変数に volatile 修飾子を付けなくてもプ ログラムの動作は正常です。そもそも C 言 語の場合も , プログラマが volatile 修飾子の " 正しい使い方 " を理解しマスターするのは 難しいといえます。この私自身も , あまり setvisible( true eSwitch = false; button = new JButton( "ln the beginning God" button. addActionLi stener ( new Experience ( ) button. setBackground( C 引 0 て . ye Ⅱ ow getContentPane( ) . add( button, BorderLayout.CENTER public static VOid main( String args[ ] void vnop( ) { / / non-static nop static VOid snop( ) { / / static nop show( int //continue; / / doesn't work / / { } / / empty block doesn't work / / empty statement doesn't work whi 厄 ( ! eSwitch ) Empty et = new Empty( / / a nonsensical statement doesn't work / / 十十 理解していませんしマスターしてもいませ ん。ただ , かねてからオプティマイジング コンパイラの問題点 , というより無能を補 うための不格好なクラッジではないかとい う印象を持っています。 もうーっの考えられる根拠は , ループ本 文のタイプ ~ 種類によって振る舞いが一定 でなく , しかも振る舞いが一定でないこと に対して合理的な理由が思い当たらないこ とです。この Sun に提出した Empty. java で は , 「何もしないループ = 待機ループ」の本 文として , Fig. 5 のものを試しています。 X 印はだめだったもの , 〇印は正常に使え たものです。 Fig. 5 の結果をどう眺めても , 納得のできる状況は何も頭に浮かんできま せん。 Thread. sleep() は static メソッドですから , static メソッドはだめだという命題も成り立 ちません。仮に static メソッドがだめなら , その理由も思い浮かびません。しかも以前 見たように , javac がコンパイルした結果で あるところの JVM'S イトコード自体は , オ プティマイジングをやっていませんから , そのぶん " 犯人の雲隠れ " に貢献してしまっ ています。 ・・ちゅうことは、 javac の " ではなくて JVM 本体の " 変数アクセスに関するオプテ イマイジングの実装に論理的一貫性がなく 無茶苦茶である。と今回 Sun は認めたのか もしれません。私の意見は , 「 Java ほどの 高級言語ともなると , 言語側で勝手なオプ ティマイジングをやるのではなく , それは プログラマ側の完全な制御事項にせよ ! 」 であります。言い換えると , 「 volatile のよ うな低レベルな ( & 分かりづらい & 使いづ らい ) 修飾子は廃止せよ ! 」であります。 この件に関する理解を助けるため , また は混乱を助長するために Fig. 6 に JI から / / another nonsens ica ー statement a 0 doesn ' t work //snop( / / static nop method doesn't work (why?? ) / / e しⅧ叩 ( / / non-static nop method mES work! //{try{Thread. sIeep(10) ;)catch( InterruptedException e) {)) et. button. C引0て . pink et. button. setText( "created the heaven and the earth. ” et. button. setEnabled( true class Experience implements ActionListener public void actionperformed( ActionEvent e ) ( / / mES or JButton button = (JButton) (). getSource( ) button. setEnabled( false Empty. eswitch = true; / * * * for your reference * * * * * / / * empty. c test the C empty statement * / / * Apr. 2000 hiwa * / / * Compile using Turbo/Borland C/C 十十 fo て lntel 80X86 * / #include く stdio. h> #include <dos . h> #def ine CopyKeyI ntNum 5 int eSwitch ー void interrupt loopEnd( ) eSwitch = 1 ー main( ) void interrupt ( *oldCopyKeyHandler) ( eSwitch = puts( "press [COPY] key tO exit on an old PC ー 98. puts( "press [Sys Rql key 社 0 て a while* in a Win-PC's X)S-Prompt." puts( [Sys RqJ is [Shiftl 十 [Print screenl . ” puts( empty statement is normal, thank you. setvect( CopyKeyIntNum, oldCopyKeyHandler whil e ( eSwitch = setvect( CopyKeyIntNum, loopEnd ); oIdCopyKeyHandIer = getvect( CopyKeyIntNum C 言語フォーラム 155

8. 月刊 C MAGAZINE 2000年9月号

吾塾 一フ習 ( グ学 コラム 3 演習 : List 4 の いままで出てきた標準関数一覧 liner—interpolation ( ) を , #define AR_MAX 10 引数の型である size t 型は unsigned であ ・ sizeof( 型名ー実体名 ) る整数型で , 処理系によって異なります。 関数ではなく単項演算子扱いである sizeo typedef struct { たいていの場合は 4 バイト以上の長さがあ f は , 与えられた型や変数 , 配列 , 構造体の int start, end, count, ります。この型で表現できる大きさまでメ 実体がメモリ上で何バイトの大きさである array [AR—MAX] ; モリを確保できるということですが , 4 バ かを求めます。 } li—params; イト符号なしでも 4G バイトまで表現できま char ch; li—params * liner—interpolation ( す。普通の人の使っているコンピュータに であるとき , は , まだそこまでメモリが載っていないと sizeof ( char) ; li—params * lip) ; 思います sizeof(ch) ; として書き直せ。 ともに 1 の値を持ちます。今回の例題では , ・ #include <stdlib. h> void free(void * ptr) ; メモリ上での占有領域を求めるために使用 動的メモリ確保とポインタ malloc ( ) によって確保したメモリを解放 したり , 動的にメモリを確保する場合に要 します。通常の処理系では、 f 「 ee ( ) を用い 求するメモリ容量を算出するのに使用して 前回 , プログラム中のいかなる場所から ずともプログラム終了時に解放が行われま います。 す。しかし , 長時間動作し続けるプログラ も参照可能なグローノヾル変数を紹介しまし ・ #include <stdlib. h> ムなどの内部で解放されないメモリのムダ void * malloc (size_t size) ; た。グローバル変数はプログラム開始時に 遣いが蓄積していくと , そのうちプログラ m 訓 oc ( ) は動的にメモリを確保します。 作成されるので , 配列をグローバルで作成 ムは動作を続けられなくなります。正しく 確保されたメモリはグローバル変数などと する場合に , 配列の要素数として想定され free ( ) を使うように心がけましよう。 同等の扱いになりますが , 変数などの宣言 る最大数で作成しなければなりません。ま 戻り値の void 型は , 値が存在しないこと とは異なり , 名前が付かないので , ポイン た , プログラムの進行具合に合わせて , 必 を示す特別な型です。関数が値を返さない タ型変数にポインタを保存して利用します。 場合や関数に引数がいらない場合に使用さ 戻り値となる確保されるメモリ領域を示 要なときに必要なだけグローバルな変数を れます。かなり古い C コンパイラでは void すポインタは , void * 型と呼ばれる , 型が 使いたい場合もあります。 型の利用できない可能性がありますが , お 不定のポインタ値となるので , 通常はキャ C 言語では , 動的メモリ確保という手法 そらくそのような特殊な環境を持っておら ストという手法で利用したい型に合わせる を使って , 必要なときに必要なだけ , グロ れる方は初心者じゃないので , ここで注思 ようにします。 List5 では int * 型に合わせ ーバル変数と同じ扱いのデータ格納領域を を喚起するまでもないでしよう ( ^ ^ るようにしています。 作成することができます。 List 5 をコンパイル , 実行してみてくださ free (p) て , ポインタとその周辺について解説しま い。実行結果は Fig. 9 と同様になります。 上記のように書いてしまうと , sizeof(in した。 malloc ( ) を使って動的メモリ確保を行う t)*MAX*(MAX-1) バイトのメモリが解放 ポインタ + ma Ⅱ oc ( ) などポインタ周辺の記 場合 , 確保される領域には名前が与えられ できなくなってしまいます。 述手法は , C 言語においてもっともバグを ません。このため , 作成した領域のポイン 含みやすい部分といえます。しかし , これ 演習 : List 5 で free ( ) は 3 行ぶんの タをポインタ型変数に保持しておき , 利用 を避けては C 言語でプログラミングするメ 記述があるが , f 「 ee ( ) の使用を 1 しなければなりません。 リットを十分に得られません。 行のみにし , なおかっ正しくメモ また , ma Ⅱ。 c ( ) によって確保された領域 私個人は , 初心者のうちはなるべくボイ は , 使い終わったら丘 ee ( ) によって明示的 リの解放を行えるようにするため ンタを使わないようなプログラミングを心 に解放してやる必要があります。 free() の がけ , 必要に迫られたときだけ使うように には , List5 をどのように書き換 したほうがよいと思います。少しずつ慣れ タイミングを正しく考えてやらないと , 確 えればよいだろう ? 保した領域を解放する前にその領域へのポ るようにして使えば , 使い方のコツも徐々 インタをなくしてしまい , メモリがムダ遣 に身につくでしよう。 まとめ いされてしまいます。 さて , 来月からは気分も一新して , より fO 「 ( i = 0 ; i く MAX ; 1 + + ) 具体的なプログラミングを見ていきたいと 今回は , C 言語の文法解説の最終回とし p=malloc(sizeof(int) * MAX) ; 思います。それでは。 ! 0 88 C MAGAZINE 2000 9

9. 月刊 C MAGAZINE 2000年9月号

真紀俊男の 第 55 回 ーテク講 ここで紹介しておきたい。 の元になる。具体的にどうすればいいかを が , やり方を間違えるとかえってトラブル しだいで完全に削除することができるのだ グローバル変数の巻 と違い , C + + ではグローバル変数は工夫 について考察する。 C 言語でのプログラム とつになった ( 笑 ) 憎むべきグローバル変数 今回は , 筆者が会社を辞めるキッカケのひ 常々筆者が主張している「グローバル変 数は削減 / 廃止すべき」であるが , この主張 の真意を誤解して , 単にグローバル変数の 「数を減らす」という点のみ着目されると困 るケースが出てくる。 たとえば , 使われているグローバル変数 のうち変数 A と変数 B と変数 C の運用範囲を 調べて , それぞれが重ならないことを確認 し , 変数 B と変数 C を変数 A に「統合」した場 合を考える。たしかに三つあったグローバ ル変数がひとつに減って , 表面的には「グ ローバル変数を削減」である。しかし , プ ログラムをあとで解析・検討する段階で , この統合された変数 A の性格が把握しにく くなってしまう。 言で意味が説明できな い変数のできあがりである。 当然のことながら , この種の「一言で意 味が説明できない」もののせいで , あとか らの保守やプログラムのバージョンアップ で苦しむハメになる。 筆者が以前に苦しんだケースではひとつ のグローバル変数に「表示用パッフア」「計 算の合計値を格納する」「計算のレジスタ代 わり」と三つ以上の役割を持たせていた。 グローバル変数の数 そのため , 表示を変更するつもりで , その グローバル変数を書き換えると , とたんに 計算の合計値がおかしくなる。苦肉の策と して , 一時的に変数の退避用の変数を作り , 変数の値をいったんそこに退避してから処 理を行うというセコい対応をした。ところ が , やはりセコい対応だったので , あとか ら仕様変更を加えると , とたんに崩れ落ち た ( 笑 ) 。 グローバル変数を多用したときの問題は 古くから指摘されており , とにかく数を減 らしなさいというアドバイス自体は正しい のだが , 間違ったアドバイスは , かえって 害になる田。 ただし , 筆者が経験したケースは「むり やりなグローノヾル変数の削減」が原因では なく , 当初はひとつの役割しか持たせてい なかった変数に , あとから別の役割を「兼 用」させたのが根本原因であろう。また , 兼用の原因はプログラマの「怠慢」にある。 つまり新たに変数を作るのがめんどうくさ かったので , それまでにあった変数を「使 いまわし」してその場をごまかしたのが原 因というわけだ この種の「再利用」は正直いって褒められ たものではないし , 再利用の意味を完全に 間違えている。 グローバル変数を 飼いならす 運用面でいえば , グローバル変数そのも のをなくすことは無理なので「必要悪」とし て認める。その代わり管理を厳しくすると いうのが現実的な選択だと思われる。チー ムで開発しているケースでは , グローバル 変数の使用を届け出制にしてしまい , どう いうグローバル変数が存在するのかを管理 者がきちんと把握しておくということであ る。この方法は , 実際に運用しているチー 外部からアクセスできないグローバル変数 ( 1 ) -ist static int glnt; void TestA(void) glnt = 0 ・ lSt 外部からアクセスできないグローバル変数 ( 2 ) 1 void TestB(void) extern int glnt; glnt = 1 ・ 真紀俊男のローテク講座 147

10. 月刊 C MAGAZINE 2000年9月号

List 8 counter, named * nc—tria12. cpp * compile()c 十十 ) counter ー ー counte 「を使う例 ( その 2 ) named-counter を使う * 0 ー—GX nc_trial.cpp counter. cpp named_counter. cpp * compiIe(BCC) : * 比 C32 nc—trial.cpp counter. cpp *. cpp cout くく″ after: をくく rc. get—value( ) くく endl; rc. increment( cout < くをくく rc. get—value( VOid inc—via—ref(counter& て 0 ) { / * 基底クラスの参照体 * / くく pc->get—value( ) くく endl• cout くく” after: つ土 nc て emen 日 cout くく "before: くく pc->get—va lue ( ) を VOid inc—via—ptr ( counter* ) ( / * 基底クラスのポインタ * / using namespace std; #include "named—counter. h" #include <iostream> return 0 ー くく nc. get—name( ) くく endl; cout くく named—counter nc("a—countern,3); 土北 main() { before: 4 afte 「 : 5 before: 3 after: 4 nc : a counte 「 Fig. 3 List 8 の実行結果 92 C MAGAZINE 2000 9 - 派生クラス基底クラス ( ) { は好ましくありません。ひとつひとつがほ トラクタ / デストラクタを inline 定義するの クラス階層が深くなりそうなときはコンス ストラクタは " 積み重ね " られますから , Fig. 1 ) 。このように , コンストラクタ / デ ラスから基底クラスの順となります (List 6 , スから派生クラス , デストラクタは派生ク される順序は , コンストラクタは基底クラ コンストラクタ , デストラクタが呼び出 自動的に呼び出されます。 スのデストラクタの処理が完了した直後に ね。基底クラスのデストラクタは派生クラ ストラクタはひとっしかありませんから スのコンストラクタで指定できますが , デ 数定義でき , そのどれを呼ぶかを派生クラ とは " 書きません " 。コンストラクタは複 List カウント値を -1 する反転カウンタ「 ev ー counte 「を作る ( 「 ev ー counte 「 . h ) ノ * * rev—counter. h #ifndef ——INCDEC—COUNTER—H—、 #define —INCDEC—COUNTER—H—- #include ” counte て . h ” class rev—counter : public counter ( public: rev—counter(int v = 0 ) ・ void increment( #endif List カウント値を -1 する反転カウンタ「 ev ー counte 「を作る (rev_counte 「 . cpp) * rev—counter. cpp #include "rev—counter. h" rev—counter; :rev—counter(int V) VOid rev—counter: :increment( ) { set—value(get—value( ) : counter(v) { んの数行だからと , inline 定義すると , そ の何倍もの " 積み重ねられた " コードが埋 め込まれてしまいますからね。 named_counter に新たに追加された set name() , get ー name ( ) については見てのと おりです。注意しなければならないのはコ ピー演算子 (List 5- [ 4 ] ) です。コピー演算 子は基底クラスのコビー演算子を自動的に 呼び出してはくれません。したがって基底 クラスである counter が持つカウント値は , 明示的にコピーしなければなりません ( Lis t 5- [ 5 ] ) 。 ・継承したクラスの検証 counter , named counter のお試しコー ド ( List7 ) を動かしてみてください ( Fig. 2 ) 。 named—counter にメソッド increment() や g et-value() を定義してなくても , counter を 継承しているので問題なく呼び出すことが できます (List 7- [ 1 ] , [ 2 ] ) 。 counter. cpp をち よっといじって increment ( ) で + 2 してみて ください。すると named_counter に対して i ncrement() しても + 2 されるはずです。継 承してますからね。 では次のお試しコードはいかがでしよう (List 8 , Fig. 3 ) 。関数 inc-via-ptr() , inc-v ia_ref() の引数はそれぞれ counter * , coun ter& です。そこに named_counter を与えて もちゃんと動いてくれます ( List & [ 1 ] , [ 2 ] ) 。 基底クラスへのポインタや参照体に派生ク ラスのそれを代入できるというルールのお 陰です。 named_counter が counter の派生 クラスでなかったら , " 型が合わない " とコ ンパイラに蹴られてしまうでしよう ( やって ごらんなさい ) 。 では練習問題。 counter を基底クラスと し , 現在のカウント値を -1 するメソッド , void decrement() ; を追加したクラス incdec_counter を作って みてください。 多態 (PoIymorphism) そんなわけで , 既存のクラスに機能を " 追加する " ということは , 継承を利用して 簡単に実現できました ( ついでながら , 機 能を " 削除する " というのは残念ながら不可 能です ) 。