Version 3.22
参照を作ろう!
「さて今回は?」
『えっと、この前〈答に数字を入れる〉ってのをしたから、今度はその数字
をレジストリのにするんだよね。でも……』
「そう、うまくいきません。ま、試してみて」
『ま、試してみたら偶然うまくいくかもしんないし』
「それはない」
『うー。えっと、前は CCalcApp::InitInstance() の中で』
CString cCompanyStr;
cCompanyStr.LoadString( IDS_REG_COMPANY );
CString cDialogStr;
cDialogStr.LoadString( IDS_REG_DIALOG );
CString cAnsStr;
cAnsStr.LoadString( IDS_REG_ANS );
SetRegistryKey( cCompanyStr );
dlg.m_iAns = GetProfileInt( cDialogStr, cAnsStr, 0 );
『って書いて、この前 CCalcDlg::OnInitDialog() で』
// TODO: 特別な初期化を行う時はこの場所に追加してください。
dlg.m_iAns = 10000;
『って書いて、こっちの方に移動すればいいんだから……』
「あ、切り取っちゃダメだよ、 cDialogStr とかはあとで使ってるんだから」
『 OK ボタン押したとこのことね』
「あと SetRegistryKey() は CCalcApp::InitInstance() で呼んだ方がいい
かな。これは〈アプリケーションの設定〉だから」
『ダイアログと関係ないってことね。じゃ、こんな感じ』
// TODO: 特別な初期化を行う時はこの場所に追加してください。
CString cDialogStr;
cDialogStr.LoadString( IDS_REG_DIALOG );
CString cAnsStr;
cAnsStr.LoadString( IDS_REG_ANS );
m_iAns = GetProfileInt( cDialogStr, cAnsStr, 0 );
『だねー。簡単簡単!』
「で?」
『はいはい。ビルドしてエラー……にならない。あれ?』
「良かったね、エラーにならなくて」
『……なんかあんね』
「じゃ、実行してみて」
『はーい……あれ、なんで【Ans】のとこが 0 なの? えっと、 100 入れ
て【OK】ボタン押して、もう一度実行……あれぇ、また 0 、なんでぇ?』
「ミスがふたつ。まず、 UpdateData( FALSE ) を付け加えて」
『え、なんで?』
「理由は次回。あともうひとつのミスは、 GetProfileInt() 。これ、さっ
きのと今のとで違う関数が呼ばれてるんだよ」
『??』
「 CCalcApp::InitInstance() で呼んだのは CWinApp::GetProfileInt() 、
CCalcDlg::OnInitDialog() で呼んだのは GetProfileInt() 、つまり API
の関数」
『い、いつのまに……』
「なんでか分かる?」
『分かんない』
「……」
『あ、えっとねぇ……』
「 CWinApp::GetProfileInt() ってことは CWinApp のメンバ関数だから?」
『あ、確か今のプログラムって CCalcDlg::OnInitDialog() の中だから』
「そう、 GetProfileInt() が属してる CWinApp クラスと別ってこと」
『だから呼べなくって、代わりに API の方が呼ばれちゃったんだ』
「じゃあ、どうすれば CWinApp の方を呼べると思う?」
『んー、たとえば dlg.GetProfileInt() みたいに使えたりするんじゃな
い?』
「当たり。じゃ、 dlg に当たるものは?」
『……えーっと』
「 GetProfileInt() はどこで使ってたの?」
『 CCalcApp::InitInstance() の中』
「そこでなんで使えるの?」
『あ、えーっと、 CCalcApp が CWinApp から継承してるからなんだよね。
CCalcDlg の中で UpdateData() とかが使えるのと同じで』
「ってことは dlg に当たるのは?」
『んー、この前言ってたよね、 CCalcDlg::OnInitDialog() の中じゃ、
CCalcDlg じゃなくて、例えば dlg 変数の中、とかで考えた方が分かりやす
いって』
「うんうん」
『ってことは、 GetProfileInt() を呼んだとき、 CCalcApp は dlg に当た
る変数になってる!』
「そうそう」
『それが分かれば CCalcDlg::OnInitDialog() の中から GetProfileInt()
を呼ぶこともできるはず!!』
「そう!」
『……で?』
「で?」
『 dlg はさ、 CCalcApp::InitInstance() の中で CCalcDlg dlg; って感じ
に変数として作ったでしょ。でも CCalcApp の変数は?』
「前に一度見たんだけどねー」
『検索っ! ……あーっ、Ver 3.1 ( No.026 )で教えてもらったんだね』
「そ、 CCalcApp::InitInstance() のすぐ上に CCalcApp theApp; ってある
でしょ、これがそう」
『まず CCalcApp の変数ができて、そのあと CCalcApp::InitInstance() が
呼ばれて、その中で CCalcDlg の変数ができて、ダイアログが表示されて、
ってやったもんねー』
「思い出してきた?」
『なんとなく。んじゃ!』
m_iAns = theApp.GetProfileInt( cDialogStr, cAnsStr, 0 );
『ビルドして実行! ほらエラーってなんでぇ??』
error C2065: 'theApp' : 定義されていない識別子です。
『なんでぇ〜!?』
「 theApp はどのファイルにある?」
『 Calc.cpp 』
「 CCalcDlg::OnInitDialog() は?」
『 CalcDlg.cpp 。もしかして、ファイルが違うから?』
「そうとも限らないでしょ。 CCalcApp::InitInstance() の中で CalcDlg
を使ってるでしょ?」
『あ、そういえば』
「つまり〈他のファイルにあるのを使う方法がある〉ってこと」
『なるほど。で、その方法とは?』
「じゃ、まず CCalcApp theApp; のあとに」
CCalcApp theApp;
// のあとに、
CCalcApp& GetTheApp()
{
return theApp;
}
// と書き足してください。
『はて? これは関数だよね……あれ?』
「Ver 2.10 ( No.021 )とか思い出してみて。普通の関数を作ったでしょ」
『あ、そういえば。メンバ関数じゃない、普通の関数』
「じゃ、思い出しながら見ていこうか。まず関数名は?」
『えっと、 GetTheApp() 。だからこれ呼べば、この関数使えるんだよね』
「じゃ、戻り値は?」
『…… CCalcApp& かな、でも最後の & って?』
「それが今回の注目点。この関数の中は?」
『えーっと、 return って〈戻り値〉っていうのを決めるんだよね。だから
GetTheApp() が theApp に置き換わる……あ、こうやると CCalcDlg からも
theApp が使えるようになるんだ!』
「まだ早いって。戻り値が CCalcApp& でしょ」
『そう、この & ってなに? theApp は CCalcApp の変数なんだから、 &
なんていらないんじゃない?』
「じゃ、取ってビルドしてみれば」
『うー、エラーでたー』
error C2558: class 'CCalcApp' : コピー コンストラクタが(略)。
「これは〈 theApp の複製は作れませんよ〉って意味」
『複製?』
「そう、コピーね。 return theApp ってすると、 theApp そのものじゃな
くて、 theApp のコピー、つまり新しく作られた CCalcApp 型の変数が、
GetTheApp() の代わりとして置かれるわけ」
『えー?? なんで theApp そのものじゃないの〜、めんどくさー』
「というわけで、戻り値の型のあとに & を付けます!」
『ってことは、これを付けると』
「 theApp そのものが返るから、コピーは作られません!」
『なるほど、そのための & なわけね』
「この & の付いた型を〈参照〉っていいます。この例なら〈 CCalcApp の
参照〉って言い方をします」
『参照って〈これはテキスト24ページを参照してください〉とかのと同
じ?』
「同じ。参照使わなかったら、そのテキストをコピーして見てもらうってこ
とだね」
『それは無駄だから参照、か〜』
「でもこれだけじゃまだダメ」
『え、そなの?』
「 GetTheApp() は Calc.cpp にあるでしょ。だからまだ CCalcDlg からは」
『呼べないって、それじゃ意味ないじゃん』
「そこで、この関数の〈宣言〉を作りましょう」
『宣言って…… GetTheApp() がここにあるぞ、使え!! ってのだよね』
「そうそう。これをヘッダーファイル、つまり Calc.h に書きます」
『そういえばそんな話あったね』
「やってみる?」
『う”』
「んじゃここは僕の方で。 Calc.h の中に CCalcApp が入ってるでしょ」
『うん、 class CCalcApp ってのでしょ』
「この CCalcApp のあとにこれを追加してください」
class CCalcApp : public CWinApp
{
// 略。
};
// この下の行を追加。
CCalcApp &GetTheApp();
『あ、そっか、関数のをそのまんま書けば良かったんだね』
「ちなみに CCalcApp のあとじゃなきゃダメだから」
『なんで?』
「 GetTheApp() の戻り値が CCalcApp だから。これ使う前に、 CCalcApp
がどういうのか分かんないとね」
『そういえばそんな話も……』
「で、 CalcDlg.cpp から Calc.h をインクルードしてるから」
『 CCalcDlg から GetTheApp() が使える!!』
「というわけで」
m_iAns = GetTheApp().GetProfileInt( cDialogStr, cAnsStr, 0 );
「で、めでたく」
『??』
「あ、ちょっと難しいかな。 GetTheApp() は theApp に置き換わるんだか
ら」
m_iAns = theApp.GetProfileInt( cDialogStr, cAnsStr, 0 );
「と同じことってことだね」
『ホントはこうはできないけど、 GetTheApp() を使えばこれと同じことが
できるってことなんだ』
「そういうこと」
/*
Preview Next Story!
*/
『って難しくないー?』
「でも、関数のとか、前にやったのばかりだけど」
『そう言われればそうだけど〜』
「難しいことも簡単な知識の積み重ね」
『大きいプログラムも小さなプログラムが組み合わさってできてる、ね』
「というわけで次回」
< Version 3.23 オーバーライド >
『につづく!!』
「今回だって、新しく教えたの参照くらいだよ」
『つまり、憶えなきゃいけないことまだまだたっぷりあるってことね……』
「憶えてくれるまで繰り返し言ってるつもりだけどね」
『はいはい』