不定期日記 (過去ログ No.5)

2005 9/28 [Acc,PG]

Java の Pluggable Look&Feel は抽象的、論理的な側面(UI 要素間の関係、個々の振る舞いなど)と、表現形式を切り離す概念です。 この思想は Java のそれに限らず他にもたくさん見られます。 例えば HTML に対する CSS の概念や X Window のウィンドウマネージャなどが挙げられるでしょう。 さて、Pluggable Look&Feel に関して Sun のサイトには次のように面白い事が書いてあります。

This architecture separates the implementation of the user interface components from their presentation. (中略). Instead of a visual presentation, a user could instead choose an audio presentation, or a tactile (e.g. Braille) presentation, or a combination of the two.

UI 要素の情報を画面に出すのではなく、 点字ディスプレイ、音声に出力するような Pluggable Look&Feel を作れるのですね。 では KGS 社の DV シリーズのような触覚ディスプレイにも情報を出せるのか。 Java には確か外部のネイティブコードを呼ぶ機能が正式にあったはずですし、 Java Accessibility Bridge の API は C のコールバック関数で Java アプリと C プログラムがやりとりするようになっています。 つまり関数をポインタで呼び出せる言語ならば問題ありません。 そして、まあ当たり前ですが DV シリーズの操作用 API は C 用に用意されています。 つまり答えは、ほぼ YES。 ではやれるだけの時間はあるのか? 半年も無い大学四年生の残り期間で終わるのか? やったとして、どれだけの価値があるのか? 調べる価値があります。

2005 9/21 [PG]

本日とうとう D&E、「C++ の設計と進化」を読み終えました。 内容は非常に充実しており、読んでいるだけでする頭が疲れてしまうほどです(笑)。 脳みその疲れに注意しながら読まないと流し読みになってしまうので、 読破に長い時間がかかりました。

さて、内容ですが間違いなく名著です。 軽々しく「名著です」なんて言うなと思うかもしれませんが、やはり名著です。 C にはなく C++ で新しく加わった要素には新しいキャスト、テンプレート、 例外処理、名前空間、そして STL などがあるわけですが、 恐らく多くの人はこれらのすべてに対し「難しい」と感じていると思います。 難しいと感じるのは(良い解説が少ない事に加えて)、 これらがどのように使われるべきなのか、 あるいはこれらが何故必要なのか、が分からないためでしょう。 この書籍には C++ の各機能が「何故そのように設計されたのか」に加え、 「何故必要とされたのか」について非常に具体的な例を交えて解説してあります。 したがって、難しいと感じる機能に関する章を読むと非常に参考になる事柄が多くあります。 こんな観点からも、多くの人々にとって読む価値があると言えます。

2005 9/20 [PG]

今日、 大学のゼミ課題に関係して画像ファイルを読み込んで表示する MFC アプリを作りました。 その際、CBitmap が画像ファイルを直接読み込めないのに業をにやして描画処理をすべて (MFC の GDI クラスを使わず) Win32API を直接呼んで書いてみました。 さて、そこから得られた結論は。

「GDI のコードは MFC アプリでも Win32API を使った方が良い」

GDI 周りの MFC クラスには隠ぺいすると災いの種になりかねない概念が隠されており、 それが原因で困ったエラー(最たるものは CDC::SetObject() の返り値を保存しておいてあとで戻さないと描画がおかしくなること) に見舞われることになります。 ではその原因はというと、 デストラクタの処理がそのコード内で見えないためであることが多い。 ならば、所詮 Win32API のラッパクラスでしかない GDI クラスを使わない方がむしろこちらがすべて把握できるので (結果の挙動と可読性の点で)分かりやすいコードになるはず、 と結論するのは難しくありません。

ではなぜ今までそうしなかったのか。 私の場合、そうするためには MFC が隠しているために今まで知らずにすんでいた Win32API の考え方を新しく勉強する必要があったからです。 つまり、それに必要な労力が予想できなかったから、 敢えて手を出さずにいました。。

しかし今日やった限りでは MFC を使わない方がむしろ早く書き終わりました。 そう、もう一つ大切なことを学んだわけです。 「挑戦せずに諦めるな」と。 言い換えると「つべこべ言う前に当たって砕けろ」(苦笑)。

ちなみに、次のような感じのコードになります (SDI アプリとしてウィザード生成したコードの OnPaint())。 当然、見ての通りすごく手抜きなコードですが、 この関数だけ書けば動くため、そのままにしておきます (画像ファイルのパスだけは変更してください(苦笑))。 まあ、自分用のメモでもありますね。

void CChildView::OnPaint()
{
    int         rc; // return code
    HDC         paintDc;
    HDC         memDc;
    PAINTSTRUCT paint = {0};
    HBITMAP     bitmap;
    BITMAP      bitmapInfo;
    HBITMAP     prevBmp;
   
    paint.hdc = GetDC()->m_hDC;
    paint.fErase = FALSE;
    GetClientRect( &paint.rcPaint );
   
    // BEGIN PAINT
    paintDc = ::BeginPaint( m_hWnd, &paint );
    if( paintDc == NULL )
    {
        return;
    }
   
    memDc = ::CreateCompatibleDC( paintDc );
   
    // load bitmap file
    bitmap = (HBITMAP)::LoadImage( NULL, "C:\\coffee.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE|LR_CREATEDIBSECTION );
    if( bitmap == NULL )
    {
        return;
    }
   
    // get the information about the bitmap
    rc = ::GetObject( bitmap, sizeof(BITMAP), &bitmapInfo );
    if( rc == 0 )
    {
        return;
    }
   
    // keep previously selected bitmap GDI-Object
    prevBmp = (HBITMAP)::SelectObject( memDc, bitmap );
    if( prevBmp == NULL )
    {
        return;
    }
   
    // flush graphic data from working dc to actual dc
    ::BitBlt( paintDc, 0, 0, bitmapInfo.bmWidth, bitmapInfo.bmHeight, memDc, 0, 0, SRCCOPY );
   
    // END PAINT
    EndPaint( &paint );
   
    ::DeleteObject( bitmap );
}

2005 9/14 [PC,PG]

一つの Windows 上で複数のコンパイラを使えるようにしていたのですが、これが案外難しかったです。 インクルードファイルの検索パス、 リンクするライブラリのパスを環境変数でしか設定できないものが多いため、 各コンパイラごとに違うライブラリ群を使いたい場合には嬉しくありません。 そこで PATH、INCLUDE、LIB という環境変数を設定するだけのバッチファイルを作ってみました。

作るにあたっていくつか注意した点があります。 最初は、各コンパイラやライブラリのパスを直接バッチファイル内に書きました。 しかし環境が変わると各コンパイラ用のバッチファイルをすべて修正する必要が出ます。 そこで各コンパイラやライブラリのパスはシステムの環境変数に設定し、 各バッチファイルはその環境変数を使って INCLUDE などを設定します。 なお使うコンパイラのパスなどが環境変数として設定されていない場合はエラーを出すようにしました。

最終的にできあがったのは次のようなバッチファイルです (Digital Mars C++ 用で、_dmc.bat としてパスの通ったディレクトリにおいています)。

@echo off

rem --------------------------------------------------------
rem   動作には次のように環境変数を定義しておく必要がある。
rem     DMC     ... DMC のインストールディレクトリ
rem     STLPort ... STLPort のインストールディレクトリ
rem     FOX     ... FOX のインストールディレクトリ
rem --------------------------------------------------------



rem ---- 各ライブラリのパスを確認 ----

rem DMC のパス
if "%DMC%"=="" goto err_no_dmc

rem STLPort のパス
if "%STLPort%"=="" goto err_no_stlport

rem FOX ツールキットのパス
if "%FOX%"=="" goto err_no_fox


cls


SET PATH=%DMC%\bin;%PATH%
SET INCLUDE=%STLPort%\stlport;%FOX%\include;%DMC%\include
SET LIB=%STLPort%\lib;%FOX%\lib\dmc;%DMC%\lib



echo ###################################
echo ### begin Digital Mars C++ mode ###
echo ###################################

goto end





rem ---- エラーメッセージ ----

:err_no_dmc
echo environment variable "DMC" not defined.
goto end

:err_no_stlport
echo environment variable "STLPort" not defined.
goto end

:err_no_fox
echo environment variable "FOX" not defined.
goto end


:end

この例の欠点は、FOX ライブラリがインストールされていなければ動作しない事ですね。 まあ手を加えてそこまで面倒を見るのも不可能ではないですが、 分かりにくくなりますし、 ぶっちゃけていって手動で環境に合わせて書き換えた方が早いので とりあえずこのまましばらく使ってみるとします。

2005 9/12 [PG]

クロスプラットフォームな GUI ツールキットを使おうかなと思い、 また MFC の GUI 周りに嫌気がさしているのもあり、 いろいろ探してみたところ候補として次の3つが挙がりました。

巷の評判や、簡単にマニュアルやサンプルソースを読んだ経験から感想を。

Qt は KDE での実績、関連情報の多さ、 それに加えて少々使った経験から、すばらしいフレームワークだと思います。 ただ、どうしても専用プリプロセッサが好きになれません。 というわけで、Qt はその他のツールキットを試した後の、最後の選択肢としたいです。 wxWidget は今ちょうど試していたのですが、アプリケーションクラスは main 関数でインスタンス化するのではなくマクロを使って宣言と実装を行うようで、 MFC にそっくりです。 メッセージマップ用の実装マクロと宣言マクロといった構成や、 その書き方もほぼ同じです。ある程度 MFC に慣れている (クラスウィザードなどを使わず手でメッセージマップなどを書ける程度) 人なら簡単に移行できそうですね。 とはいえ私は main 関数を自分で書けないのが気持ち悪いと感じてしまうので(笑)、 Qt や FOX、Carbon (MacOS X のネイティブなフレームワーク) のように main 関数の中でアプリケーションクラスを作る方が性に合いそうです。 次は FOX を試してみようと思っていますが、 恐らく FOX が一番好きになりそうですね。 私が好きな Digital Mars C++ でコンパイルできるかどうか(笑)。

2005 8/21 [PG]

OPT プラグイン Randomizer。 ふたたびアップデートです。

今回の更新内容は私にとって巨大なものになりました。 更新内容の中で一番大きいのは、 COM インターフェイスの実装方法を MFC が採用しているネストクラスによる実装から 多重継承による実装に変更した事です。 これによって MFC が提供する大量のマクロが無くなり、 よりソースを読みやすくなりました。 Randomizer は勉強をかねて作ったものですが、 現れるかもしれない OPT プラグインを作りたいと思う誰かのために読みやすく書いています。 特に COM のローレベルな部分を知りたい人にとって、 今の世の中はいまいち優しくありません。 MFC を使わずに「素」で OPT を実装した例は貴重だと思いますし、 またこれを見る事で各フレームワークが提供する COM 関係の API が何をしているのか、少しでもつかみやすくなればいいなと思っています。

とにもかくにもこの作業は MFC のハックでした。 COM 関係のマクロや API の仕様をはじめ、 MFC の「モジュール状態(state)」とリソースハンドラについてまで 調べるはめになってしまって正直うんざり(苦笑)。 COM のローレベルな部分についての資料はインターネット上にはあまり多くありません (MFC を前提としたものならいくらでも見つかりますが)。 それに加えて MFC のモジュール状態周りは驚くほど情報がありませんでしたから、 安直に他人の答えを探すより自分でソースを読む方が早いという結論に達してしまいました。 CCmdTarget はメンバ変数に初期化時のモジュール状態を記憶するとか、 リソースハンドルの取得は AfxSetModuleState() を使ってモジュール状態をそのリソースが格納されているモジュールのものに変更してから行わないといけないとか、 AfxSetModuleState() は MSDN に載ってないとか、 AfxGetStaticModuleState() はグローバル変数から値を取ってきているとか、 その変数は最終的にプログラムとリンクされる MFC のライブラリに埋め込まれている DllMain() が勝手に設定してくれるとか、GetModuleFileName() で適当に実行ファイルのフルパスを得ようとするとプラグインの場合はホストアプリのパスがとれてしまうとか、 その対策にプラグイン DLL のモジュールハンドルを渡そうと思ってもどこで手に入るのか分かりにくいとか、 それは DllMain() で受け取るものだとか、 MFC を使う DLL では DllMain() がライブラリに埋め込まれているのでどうしようとか、 DllMain() が保存する「モジュール状態」のクラス AFX_MODULE_STATE の中には DLL のモジュールハンドルがちゃんと格納されているとか、 それを AfxGetStaticModuleState() で取得すれば正しくパスがとれるとか、 ・・・まあ、おかげさまで不覚にも MFC にかなり詳しくなってしまいました(苦笑)。

さて話を戻しまして、今回の更新内容でその次に大きいのは MacOS X を考えて MFC/Win32API への依存をかなり小さくした事です。 まあ COM 周りを自前実装した時点で依存の大部分は無くなったのですが。 GUI をのぞけば数カ所の修正でそのまま動かせるでしょう・・・たぶん。 プラットフォーム固有の API 等を使う部分はラッパー (関数かクラス) を一枚かぶせていますし、 COM 周りの処理は MFC を使わずにクラスファクトリからすべて自作しました。 さて、これで後は検証に使える MacOS X の OPT クライアントを手に入れるだけ・・・ と思っていたところ、 最近 Studio Connections のサイトを見ていない事を思い出しました。 そこで SDK のダウンロードページにジャンプしてみると、 SDK が 1.0.2 にアップデートされているじゃあないですか。

ダウンロードが終わって今、仕様とか見ているわけですが・・・。 うーん、SDK 1.0.0 を手に入れた時点で分かっていた事ですが、 YAMAHA は自社の開発に Qt を導入したんですよね。 クロスプラットフォームで GUI を作るとなると、 現実的な解答は Qt ぐらいしかありませんのでそれ自体は何も不思議な事ではありません。 しかし、何故か SDK に付いてきた Studio Manager が「スクリーンリーダーで読み上げられない」のが気になります。 私が試した限り、Qt (for Windows 4.0.0) で作ったウィンドウはメニューなどの基本的な UI 要素はスクリーンリーダーで読み上げられましたし、 Qt のサイトでもアクセシビリティ機能として Windows 版 Qt のウィジェットは MSAA へ対応していると書かれていたのを記憶しています。 ・・・って、あ、もしかして Qt 3 は MSAA 非対応なのかな? 時期的には Qt 4 for Win がリリースされたのはごくごく最近なので YAMAHA が すでに Qt 4 を使っている可能性は低いですから、そういう事なのでしょうね。

さて・・・Qt・・・か。 ・・・専用プリプロセッサが玉に瑕なんだよなぁ・・・(ボソ。

2005 8/8 [音楽,PG]

新曲 gently を公開デス。 一応 Fate/stay night というゲームの BGM をアレンジした曲です。 もしよろしければ音楽のページからどうぞ。

OPT プラグイン Randomizer をアップデートしました。 バグ修正と、ソース上の改善です。

2005 7/27 [Web]

HTML 4.01 の仕様書を見ていたら当サイトのコンテンツ 「HTML の Entity 一覧」に抜けているものがたくさんある事に気が付きました。 なので追加しました。 抜けていたのは HTML 4.01 のページ "Character entity references in HTML 4" 中、"markup-significant and internationalization characters" のエンティティ「全部」です(三分の一が抜けていた)。 今となっては覚えてないくらい昔に書いたコンテンツですがすごい手落ちです(汗)。 一覧に " が無い時点で気づくべきですよね・・・(苦笑)。

2005 7/18 [PC]

最近デスクトップ PC で使っているキーボードの右 Ctrl の調子が悪く、見た目も黄色くなってしまっているので密かに買い換えを考えています。 とはいえなかなか気に入るキーボードが無いんですね。 しかしここに来てついに心から欲しいと思わせるキーボードを見つけました。 Optimus keyboard という製品なのですが、 これは全キートップがカラーディスプレイになっているため、 見た目上どんな言語用にでも使えます (内部的にキーコードを再割り当てできるかどうかまでは調べてません)。 また、「カラー」ディスプレイだけあって、 キートップへの表示は文字だけでなく画像も可能。 紹介記事にある Quake 3 用の例を見る限り、 雰囲気は文句の付けようも無いほどすばらしい。 まあこのあたりは写真屋の腕がかなり影響しているわけでもありますが。 ・・・高くなければ買おうかなぁ。

2005 7/10 [日常]

今日は何の日かって?そりゃ厄日さ。

ところで OPT 対応リストエディタですが、完成のメドが立ちました。 実装時に設計方針を見失ったり、設計そのものが迷走したりしないようにするため、 ゆっくりと進めています。 すでにソース総容量が 300KB を超えているので正直手に余ってきている感がありますが、 まあ気持ちが悪かったところを気持ち悪くない形にできる方法を見つけたので、 大丈夫でしょう。

2005 6/13 [音楽,PC]

注文していた CD たちが amazon から届きました。 次の CD たちです。

RE KJM は Kyoto Jazz Massive の十周年記念トリビュートアルバムですね。 とりあえず聴いたところでは、やはり eclipse は良い。 このアルバムも、これから聴き込んでいくのが楽しみです。 Four Tet も相変わらずすばらしい音楽を作ってくれています。 このアルバムもすごく良いです。 そして買うまでかなり未知数だった TRS-80 ですね。 個人的には・・・かなり感性が近いです。カッコイイです。 そしてなぜか 6 曲目が motoki。 日本語っぽい曲名なのが気になりますね(苦笑)。 ちなみにレイハラカミはまだ聴いてません。 最後にじっくり聴こうと思います。

ところで、マウスパッドを新しく買いました。 本当は AirPad Pro (普通サイズ)を買いたかったのですが、この製品は半透明のものしかないんですね。 店頭で見るまで失念していましたが、 私が今使っている机のキーボードトレーは透明なアクリル板なので半透明のマウスパッドだと光学式マウスが使えません。 という事で、かわりに ELECOM の MP-081BK という製品に変更しました。 さっそく Unreal Tournament 2004 で試したところ・・・ あいや、正直なところ何も変わってないですね(苦笑)。

2005 5/28 [SOL]

ボイスリストの作り方」を書き直しました。 全体的に見ればよりしっかりした解説になった・・・と思います。 思うだけですけれども(苦笑)。

2005 5/26 [SOL]

「SOL 用ボイスリストの作り方」というコンテンツがあるわけなのですが、 非常に分かりにくいなと思っていました(苦笑)。 ずーーーーーっと思っていましたので、修正してます。 最初に書いた頃に分からなかった事でも OPT プログラミングを通して分かるようになったものもありますしね。 修正後はもっと文書として価値のあるものにしたいです。

2005 5/20 [OPT]

OPT プラグインを SOL2 で動作チェックしながら開発しているのですが、 どうにも分からない事が出てきています。

まず IMPEventList::MPSet() の使い方がさっぱり分かりません。 MPSet() には値を変更したいデータの ID 、 種類、変更後の値、変更する値の種類と四つの引数を渡すわけなのですが、 何が悪いのかこの関数、一度も成功しないんですよ。 まず、ID は MPGet() に渡すとデータ取得に成功するので正常です。 その他の引数はまず間違えようがないと思われます。 書き込みだからちゃんと Lock しないとだめなのかと思って MPLock() を呼んでから実行すると SOL ごと止まってしまいます ・・・なんでだ?(苦笑)。

ところで SOL の実装について一点気が付いた事があります。 MPLock() を呼んでいない状態で MPUnlock() を呼ぶと「成功してしまう」のですね。 できることなら、これは修正した方が良いと思います・・・。 MPLock() が呼ばれているかどうかなんて判定処理、 (まともに設計されていれば)たいしたことの無い作業量でしょうしね。 間違って呼ぶと SOL ごと固まってしまうようですので少々嬉しくないです (まあプログラムエラーなので完全にこちらが悪いわけですが、普通は「ちゃんと失敗」するかと)。

また、SOL の仕様についても一点気が付いた事があります。 MPGetNext() が、次のデータが無い場合に「成功」の意味である S_OK を返すのは仕様として間違いだと思います。 「関数」とは「機能」であるので「関数の成功」とは「その機能の成功」です。 MPGetNext() の機能は「指定データの次を取得する」事なので、 その成功とは「指定データの次のデータを取得できた」事を指すのが普通です。 ならば「(プログラム的に)正常に、次のデータを取得できなかった」 場合は失敗、でしょう。 こういった場合、関数の処理自体は正常に終了するので S_OK を返したのだと思いますが、 それは関数作者の側から見た解釈であって、呼ぶ側から見れば絶対に間違っています。 たまたま MPGetNext() の引数に searchID があるので、それを 0 にする事で呼び出し側に「次が無い」事を知らせる事ができますが、 そこで判定するのは不自然です。

追伸 @ 25 時 23 分。 なぜか MPSet() ができるようになりました。 憶測にすぎませんが、もしかすると MPLock() を呼んでから MPUnlock() を呼ぶまでの間にデバッグ用のメッセージボックスを表示させていたせいで ロック時間が非常に長くなっていた事のが原因なのかもしれません。 ・・・でも、そんな事あるのかなぁ・・・(苦笑)。

さらに追伸 @ 次の日の 17 時 40 分。 「正常に取得できない場合に成功する」のは MPGetNext() ではなく MPGetFirst() でした(汗)。

2005 5/8 [SOL,PG]

SOL (と OPT の MIDI エフェクトが使えるシーケンサ) 用エフェクトプラグイン Randomizer をバージョンアップ! 内容は・・・ダイアログをちょっと修正。 ソースを一部書き直しました。つまり機能に変化はありません、と(苦笑)。

リストエディタがだいぶできあがってきました。 データの表示についてはほぼ完成していて、 例えば指定トラックにあるブロック名と開始位置、長さを一覧、 リンクブロックであればリンク先も一緒に表示できています。 残るはリストにエディットボックスを埋め込む事と、各種設定ダイアログの設計と実装。 設計以外は退屈この上ない作業ですね(苦笑)。

4 月 28 日に、OPT のコードについて「こんなコードが書けたらなぁ」と書いていたわけですが、 SequenceObject というクラスを作った結果、結構近いものができました。 次のような感じです。

int            rc; // return code
char           name[255];
SequenceObject song;
SequenceObject track;

// オブジェクトツリーのルート(ソング)を取得
song = SequenceObject::GetSongObject();
if( song.IsValid() == false )
{
   return false;
}

// このソングのトラック名を次々とメッセージボックスに表示する
track = song.GetFirstMemberObject();
while( track.IsValid() )
{
   // トラック名を取得、Unicode から文字コードを変換
   wcstombs( name, track.m_Name, 255 );
   
   // トラック名を表示
   MessageBox( this->m_hWnd, name, "トラック名", MB_OK );
   
   // 次のトラックへ
   track = track.GetNextObject();
}

そもそもプラグインがユーザーに対して提供しようとしているのは ソングの中にトラックが、トラックの中にブロックが、 ブロックの中にイベントがある、というディレクトリシステムやフラクタル図形のような世界です。 これは OPT のデータモデルと非常にマッチしているのですが、OPT の API がトラックとブロックの区別無くそのメンバを取得できるか?というとなっていないわけです (普通はそんな使い方をしないので当然といえば当然)。 という事は、それらの区別を吸収する機能がプラグインのどこかに必要になります。 そこで、まあいろいろと考えた上で SequenceObject というクラスを作ってこれに全部差を吸収させました。 ただ、あまり複雑なものを作ってしまうと OPT のプログラムではなく 「私の」プログラムになってしまうので躊躇はしたのですが・・・ 結局その差の吸収用ロジックはどう実装したところでプロジェクト中のどこかに現れるので、 コンセプトさえ分かれば SequenceObject を導入した方がコードを理解しやすくなるだろう、という判断です。

あとはオーナー描画リストにエディットボックスを埋め込んでインライン編集できるようになれば 基本的な機能が完成しますね。 しかしそうこうしているうちに大学院入試も近づいてきているし・・・ 時間が足りるかどうか(苦笑)。

2005 5/2 [PG]

内容はさておき、C++ のプログラムで例えば次のようなクラスと関数があり、main() 関数にあるような使い方をしたとします。

class UselessString
{
    public:
        UselessString( const char * string )
        {
            m_String = new char[strlen(string)+1];
            strcpy( m_String, string );
        }

        ~UselessString()
        {
            delete m_String;
        }

        char * m_String;
};

void MyPrintf( UselessString str )
{
    printf( "%s\n", str.m_String );
}

void main()
{
    UselessString str( "is really useless!" );

    MyPrintf( str );
    MyPrintf( str );
}

実行結果は次のようになるでしょうか。

is really useless!
is really useless!

答えは、「こうなりません」。 どうなるかというと、is really useless! というメッセージは一回しか表示されず、 二回目の MyPrintf() は意味不明な文字列を表示します。 何か妙な感じがしますね。

この仕組みは次のようなものです。 一般の C/C++ では関数の引数は値引数であり、 呼び出された関数内部では暗黙に作られた引数のコピーにしかアクセスできず、 渡された変数そのものにはアクセスできないようになっています。 構造体やクラスを値引数で渡した場合もやはりその変数のコピーが暗黙のうちに作られるのですが、 ここで「コピー」の意味を考える必要があります。 値渡しで作られるコピーは、変数全ビットをそっくりコピーしたものです。 したがって例の UselessString クラスを値引数で渡した場合、 メンバの m_String は同じ領域を指す事になります。 そして一回目の MyPrintf() が終了した時、 暗黙に作られた引数のコピーは破棄されます。 つまり、この時点でUselessString のデコンストラクタが呼ばれてしまうため、 m_String が指していた領域が削除されます。 これがこの「妙な動作」のカラクリ、です。

いや、言われてみれば何度も聞いた事がある話ではありますし、 そんなに難しい話でも無いありません。 しかし関数の作者は、 値渡しは呼び出し側に「呼び出した結果、絶対呼び出し側に影響は出ない」事を保証するために使います。 このクラスを値渡しするとこの原則(?)が崩れてしまうので気持ち悪いですね・・・。 うーん、こういうのを回避する方法ってあるのかな・・・ (参照渡し/ポインタ渡しすればいい、とかではなくクラスの設計方法)。

まあ今日うかつにもこれと同じミスをして時間を少々使ってしまいました。 まだまだ未熟。

2005 5/1 [icon]

音ファイル用アイコンを一つ追加しました。 .mka (matroska audio) 用です。 入り用の方はアイコンのページからどうぞ。

ナビゲータ

一つ新しいログ | 一つ古いログ