Version 3.23
オーバーライド
「前回までに、ダイアログを表示したときに【Ans】の中に前の結果を入れ
ておく、って機能を CCalcDlg に移す、っていうことをしました」
『ってことは、残るは【OK】ボタンを押したとき、だね』
「そゆこと。さて、ボタンを押したとき、って言ったらどんな感じだと思
う?」
『そりゃやっぱり、【=】ボタンが押したときに CCalcDlg::OnBEqual() が
呼ばれる、みたいな感じなんじゃないかな』
「その通り。で、そのときはどうしたんだっけ」
『えーっと、かなり昔だよね』
「そう、かなり昔…… Ver 2.0 ( No.011 )だね」
『でもまだ1年経ってないんだ』
「これ入れてあと3回で1周年。ま、それは置いといて、もう一度復習を兼
ねて〈イベントハンドラ〉の作り方を見てみましょう」
『あ、こういうのもイベントハンドラって言うんだ』
「さて、まずダイアログエディタで Calc ダイアログ表示して」
『 ResourceView で IDD_CALC_DIALOG をダブルクリックと』
「そしたら【OK】ボタンの右クリックメニューから」
『【イベント】でしょ!』
「そう。それを選ぶと」
【クラス CCalcDlg 用の Windows メッセージおよびイベント ハンドラの
新規作成】というダイアログが表示される。
「左側が BN_CLICKED に、右下が IDOK になってることを確認して」
『あ、そういえば BN_CLICKED ってなに?』
「うーん、まだ早いような……」
『とりあえずおせーて』
「そうだね、たとえばボタンが押されたときとかのイベントは、ウィンドウ
ズが管理してます」
『ってことは API とかってこと?』
「うん、そういうレベルでサポートされてるってこと。で、実際にボタンを
押すと、ウィンドウズからアプリケーションに BN_CLICKED っていう〈メッ
セージ〉が送られてきます」
『めっせーじ?』
「ま、〈ボタンを押したぞ!〉って声、みたいなもんだね。このほかにも、
イベントの数だけメッセージがあって、 MFC を使わないときにはこのメッ
セージをかなり憶えておかないとちょっときつかったり……」
『へー、そーゆーのも MFC 使うと楽できるんだ』
「でもここで BN_CLICKED 知らないとまずかったりとか、 MFC があれば全
然OKってわけでもないんだけどね。ま、ようするにこれから憶えることが
たくさんあるってことで」
『む〜』
「で、右下の IDOK はボタンの ID ね」
『それはわかる。で、【追加と編集】ボタンを押せばいいんでしょ?』
「そゆこと」
ボタンを押すと【メンバ関数の追加】ダイアログが表示される。
『メンバ関数名、 OnOK() 。このまんまで?』
「このまんまで」
『んじゃまんま。……あれ、 OnOK() ってどっかで……』
OK ボタンを押すと CalcDlg.cpp が表示される。
void CCalcDlg::OnOK()
{
// TODO: この位置にその他の検証用のコードを追加してください
CDialog::OnOK();
}
『あ、こんなところにも TODO が』
「さて、【OK】ボタンを押したらこの関数が呼ばれるってことを確認したい
とき、どうするんだっけ?」
『えっと、 TRACE() 置くとか、ブレークポイント設置するとか?』
「そうそう。今回はブレークポイントの方を使ってみて」
『はーい。んじゃ TODO を』
「ちょいまち、ブレークポイントは〈プログラムのある行〉じゃないと。だ
からコメントの上じゃダメ」
『へーそうなんだ。じゃー CDialog::OnOK() をクリックして、手のひらボ
タンを押して』
行の左に丸が表れる。
『んじゃビルドして実行、【OK】ボタンをクリック!』
丸の上に右向き矢印が表れ、アプリケーションが停止する。
『止まったから、やっぱ【OK】ボタン押すとこれ呼ばれるんだ』
「じゃ続いて、 CDialog::OnOK() の中に入ってみて」
『あら予想外の展開。んじゃ中カッコに入るボタンっと』
Dlgcore.cpp ファイルが表示される。
『あ、 CDialog のメンバ関数だから、 MFC のプログラムのファイルが表示
されたんだね。……あれ?』
CDialog::OnOK() が表示されている。
『これって、前に一度見たことない?』
「あるよ、 Ver 3.17 ( No.042 )で。あのときはダイアログを閉じるんで」
『そうそう、ダイアログを閉じるのは EndDialog() っていうので、その引
数が DoModal() の戻り値になる、とかやったよね、うんうん』
「さて、考えてみましょう」
『はい?』
「【OK】ボタンを押したときに呼ばれたのは?」
『 CCalcDlg::OnOK() だよ』
「じゃあ CDialog::OnOK() は?」
『……そういえば、 CDialog::OnOK(); っていう行が CCalcDlg::OnOK()
の中にあって、そこに入ったら……』
「じゃ、実験。この行をコメントアウトしてみて」
『ってことは』
void CCalcDlg::OnOK()
{
// CDialog::OnOK();
}
『って感じ?』
「そんな感じ」
『じゃビルドして実行……あーっ、【OK】ボタン押してもダイアログ閉じな
いー!!』
「そういうことになります。つまり……どしたの?」
『どうやってダイアログ閉じればいいのぉ?』
「【キャンセル】ボタンは効くでしょ」
『あ、そっか』
「で、どういうことだと思う?」
『つまり、 CDialog::OnOK(); って行では、 CDialog クラスの OnOK() メ
ンバ関数を呼んでるってことなんだ』
「それはつまり、 CDialog と CCalcDlg の両方に OnOK() メンバ関数があ
るってことだよね」
『あそうか! で、 CDialog::OnOK() ひとつだったときにはこれが呼ばれ
て、 CCalcDlg::OnOK() ができたらこの新しい方が呼ばれるようになった』
「重要なのは、かたっぽしか呼ばれないってこと」
『つまりわざわざ CDialog::OnOK(); ってしないと呼ばれないんだ』
「こういうシステムを〈オーバーライド〉っていいます」
『おーばーらいど?』
「そう。まず、 MFC の方に【OK】ボタンを押したとき用のイベントハンド
ラ CDialog::OnOK() を用意しておきます」
『うんうん』
「このメンバ関数にはダイアログを閉じる機能を入れておきます」
『そうすれば、ダイアログに IDOK のボタンを置くと、そのボタンを押す
だけでこのメンバ関数が呼ばれてダイアログが閉じる、って仕組みなんだ
よね』
「そうそう。これって簡単でしょ」
『うん、簡単』
「でも、今回みたいに〈【OK】ボタンを押したときに独自の処理をしたい〉
っていう場合があるでしょ」
『そういうときに、 CCalcDlg::OnOK() を作る!』
「継承したクラスで、同じ名前のメンバ関数を作ると、新しく作った方が優
先的に呼ばれる仕組みになっているわけ」
『それがオーバーライド?』
「そういうこと。あと〈 OnOK() を CCalcDlg クラスでオーバーライドする〉
みたいに使う時もあるかな」
『へー。あ、もしかしてこの辺って、前に話してた〈カスタマイズクラス〉
とかと関係ある?』
「もちろん。 MFC にある機能を使いながら、そのアプリケーション専用の
機能を組み込む。そのために」
『 MFC のクラスから継承した新しいクラスを作って、こんなふうにオー
バーライドすればいいんだ』
「ま、これは思いっきり簡単な例だけどね。あ、今のコメントアウト取り除
いておいてね」
『はーい。で、【OK】ボタンのをここに書き込むんでしょ?』
「そうそう。これまでの知識があればできるはずだけど……」
『やってみる!! えっと、まず CCalcApp::InitInstance() の中で……』
if (nResponse == IDOK)
{
WriteProfileInt( cDialogStr, cAnsStr, dlg.m_iAns );
}
『この WriteProfileInt() を切って貼っつけちゃえばいいんだから……』
void CCalcDlg::OnOK()
{
WriteProfileInt( cDialogStr, cAnsStr, dlg.m_iAns );
CDialog::OnOK();
}
『で……どうしよ』
「ひとつずつひとつずつ」
『はぁい。じゃ、最後の m_iAns から。ここは CCalcDlg 、ううん dlg の
中なんだから、 dlg. はいらないから……』
WriteProfileInt( cDialogStr, cAnsStr, m_iAns );
『 cDialogStr と cAnsStr は CCalcApp::InitInstance() の中で作ってた
んだよね。あれを……カットしちゃっていい?』
「うん、大丈夫。もう CCalcApp::InitInstance() の中じゃ使ってないから
ね」
『じゃ……』
void CCalcDlg::OnOK()
{
CString cDialogStr;
cDialogStr.LoadString( IDS_REG_DIALOG );
CString cAnsStr;
cAnsStr.LoadString( IDS_REG_ANS );
WriteProfileInt( cDialogStr, cAnsStr, m_iAns );
CDialog::OnOK();
}
『最後に、前回作った GetTheApp() を使って……』
「あ、あと UpdateData( TRUE ) も呼んでおいて。そうしないと m_iAns の
中に値が入ってないから」
『じゃっ!』
void CCalcDlg::OnOK()
{
UpdateData( TRUE );
CString cDialogStr;
cDialogStr.LoadString( IDS_REG_DIALOG );
CString cAnsStr;
cAnsStr.LoadString( IDS_REG_ANS );
GetTheApp().WriteProfileInt( cDialogStr, cAnsStr, m_iAns );
CDialog::OnOK();
}
『どう? どうよ、これで!』
「OK!」
『じゃ、ビルドして実行、計算して、【OK】ボタン押して閉じて、もう一度
実行……やたっ、同じのが【Ans】に入ってる!』
「うん、大丈夫そうだね」
『でもなんで今度は UpdateData() 必要なの? CCalcDlg::OnInitDialog()
のときもそうだったよね』
「呼ばないで済んだのは CDialog::OnOK() の中で呼んでくれてたから。も
う一度 CDialog::OnOK() の中見てみ?」
『あ、ホントだ、呼んでる』
「同じく CCalcDlg::OnInitDialog() の方じゃ、 CDialog::OnInitDialog()
の方で呼んでくれてたってこと」
『なるほど。ってことはさ、』
void CCalcDlg::OnOK()
{
CDialog::OnOK();
CString cDialogStr;
cDialogStr.LoadString( IDS_REG_DIALOG );
CString cAnsStr;
cAnsStr.LoadString( IDS_REG_ANS );
GetTheApp().WriteProfileInt( cDialogStr, cAnsStr, m_iAns );
}
『なんてのはどう?』
「それもOK!」
/*
Preview Next Story!
*/
『そういえば、あと2回?』
「あと2回で1周年。Ver 3 もそこで終了」
『って、 Ver 3 って半年近くやってるんだね』
「それだけ、重要な話が詰まってたってことだね」
『無駄なとこも多かったような……』
「というわけで次回」
< Version 3.24 デバッグビルドとリリースビルド >
『につづく!!』
「 Ver 4 からは細かい話」
『って、あと2回もしっかり頼むよー』
「はいはい」