Version 6.16
気に掛ける範囲を狭める!
「前回、バグをコンパイルエラーで見つける例を紹介して、その最後で〈気
に掛ける範囲〉についてちょっと触れました」
『でもよく分かんなかった』
「そだね、〈気に掛ける範囲〉ってちょっと分かりにくいよね。そこで、変
数のスコープを復習しながら、ゆっくり見ていきましょう」
『ゆっくり、ねぇ』
「そう、大事なとこだしね。まずは〈グローバル変数〉から。おなじみ
CDebugDlg::OnButton1() の入ってるファイルの上の方」
// DebugDlg.cpp : インプリメンテーション ファイル
//
#include "stdafx.h"
#include "Debug.h"
#include "DebugDlg.h"
int g_i; // ここに追加。
#ifdef _DEBUG
// 以下略
『そういえばこんなとこにも変数作れるんだよね』
「こんなとこに作る変数を〈グローバル変数〉って言います。グローバルっ
て名の通り、この g_i は DebugDlg.cpp の中ならどこからでも使えます」
void CDebugDlg::OnButton1()
{
g_i = 500;
TRACE( "%d\n", g_i );
// 500
}
『ってこれは CDebugDlg::OnButton1() で使ってるけど、他でも使える?』
「もちろん」
CAboutDlg::CAboutDlg() : CDialog(CAboutDlg::IDD)
{
g_i = 500;
TRACE( "%d\n", g_i );
// 500
//{{AFX_DATA_INIT(CAboutDlg)
//}}AFX_DATA_INIT
}
『げ、普段気にも留めてなかった謎の関数の中で……』
「これは CAboutDlg のだから、試す時はダイアログの左上のアイコンを右
クリックしてバージョン情報を表示しないといけないから」
『ホントだ、ちゃんと出た……』
「こんなふうに、グローバル変数は、作られたファイルの中で、作られた後
のどの関数からも使えます」
『なんか便利そう……』
「が! その分注意しなきゃいけないんです」
『どゆこと? それが〈気に掛ける範囲〉ってヤツ?』
「そうなんです。もしこういうふうに作ったら、 g_i を書き換えられる範
囲がファイル全体に広がります。ってことは、いつ書き換えられるか心配し
なきゃいけない範囲が広がるってこと」
『なんかイメージ掴みにくいけど……』
「もし火美ちゃんが、今の g_i をダイアログ用に使おうと思ってたとしま
す」
『思いました!』
「そのときついうっかり CAboutDlg のメンバ関数で値を書き換えちゃった
ら……」
『そりゃまずい。でも、ついうっかり?』
「もしくはわざと。さっき火美ちゃん〈便利そう〉って言ったでしょ」
『うん言った。そっか、 CAboutDlg で書き換えられるのが便利、って思っ
て使うことあるかも、ってこと?』
「そゆこと。今は関数も変数も少ないけど、ちょっと本格的なプログラムに
なるとどばっと増えて、その増えた分だけ気に掛ける変数も、気に掛ける範
囲も広がっちゃうんです。そしてそこに、バグが生まれる!」
『おお! つまり書き間違えちゃったりしやすいってことね』
「だから、まずグローバル変数は作っちゃダメ!」
『ダメなんすか』
「そう、ダメ。絶対ダメ、って言ってもいいくらいダメ」
『なんか押し強いね、今日は……』
「まーでも難しい部分もあるんだけど……それが、グローバル変数の次に心
配する範囲が広いメンバ変数」
『メンバ変数って、クラスの中でしか使えない変数だよね』
「そうそう。計算機作ったりリストボックス使ったりしたときにやったけ
ど、もう一度ちゃんと見ておこうか。今度は DebugDlg.h の中身」
// DebugDlg.h : ヘッダー ファイル
//
// 略
class CDebugDlg : public CDialog
{
int m_i;
// 以下略。
『って、前、たしか Ver 6.04 ( No.104 ) で m_cIFStrm 作ったとこじゃ
ん。っつーかまだ残ってるし』
「じゃあそれ削除してこれに置き換えて。まあ、メンバ変数ってことで言え
ば m_i も m_cIFStrm も変わらないけどね」
『で、この m_i はメンバ変数になるんだから、メンバ関数ならどこからで
も使えるんだよね。たとえばいつもの CDebugDlg::OnButton1() で……』
void CDebugDlg::OnButton1()
{
m_i = 500;
TRACE( "%d\n", m_i );
// 500
}
『うんうん』
「でも、さっきの CAboutDlg::CAboutDlg() の中じゃ使えないんだよ」
CAboutDlg::CAboutDlg() : CDialog(CAboutDlg::IDD)
{
m_i = 500; // コンパイルエラー。
//{{AFX_DATA_INIT(CAboutDlg)
//}}AFX_DATA_INIT
}
『そっか、 これは CDebugDlg クラスのメンバ関数じゃないから』
「そう。でもさ、これも CAboutDlg の中で使えないだけで、 CDebugDlg の
中からはどこからでも使えるから」
『さっき言ったみたいに、 CDebugDlg が大きくなったり、 m_i みたいなメ
ンバ変数が増えたら大変になっちゃうわけね』
「そゆこと。そうなってきたら、クラスを分割して分けます」
『……なにそれ』
「まー、ようするに新しく自分でクラスを作って、その中にメンバ変数やメ
ンバ関数を移すってこと。これはまた難しい話になってくるから、先に進ん
でからじっくりとね」
『いつものパターン……』
「で、メンバ変数よりも範囲が狭いのが、普通の、つまりローカル変数」
void CDebugDlg::OnButton1()
{
int i = 500;
TRACE( "%d\n", i );
// 500
}
『いつものヤツね。この i は他のメンバ関数からも呼べないわけね』
「そう。もし使いたい場合には、引数として渡せばいいから」
『なるほど』
「さて、ここからがある意味本題。たとえば文字列ポインタ」
void TraceChars( char *p_ch )
{
TRACE( "%s\n", p_ch ); // ABCDE
}
void CDebugDlg::OnButton1()
{
char ch[] = "ABCDE";
TraceChars( ch );
}
『文字列渡して出力してるね』
「これをうっかり書き換えて……」
void TraceChars( char *p_ch )
{
TRACE( "%s\n", p_ch ); // ABCDE
p_ch[0] = 'Z';
}
void CDebugDlg::OnButton1()
{
char ch[] = "ABCDE";
TraceChars( ch );
TRACE( "%s\n", ch ); // ZBCDE
}
『あ、 "ABCDE" の中身が書き変わっちゃった』
「もちろん、これが TraceChars() の機能ってことならいいんだけど、そう
じゃなくてこれは単なる間違い、 TraceChars() の中では書き換えないよう
にする、って場合には?」
『 const ポインタ!』
「そうでした」
void TraceChars( const char *const p_ch )
{
p_ch[0] = 'Z'; // コンパイルエラー
}
『そうそう、こうすれば配列の中身を書き換えられないんだよね』
「そしてこれが、範囲を狭めるってこと」
『どゆこと?』
「 const じゃなかった場合、 CDebugDlg::OnButton1() の中の ch が書き
換えられる範囲は、 CDebugDlg::OnButton1() と TraceChars() の両方だっ
たわけ」
『そっか、それを const にすれば TraceChars() じゃ書き換えられなくな
るから書き換えられちゃう範囲が小さくなる、つまり気に掛ける範囲が狭ま
る!』
「そゆこと。こうやって狭く狭くしていけば、それだけ楽になるから」
『そういえば const char *const のふたつめの const は? 確か……これ
すると、ポインタが変わらなくなるんだよね。だから』
void TraceChars( const char *const p_ch )
{
p_ch = NULL; // コンパイルエラー
}
『なんだよね。でもこれって意味なくない? これができても、外の ch に
は関係ないような気がするし……』
「もちろん外には関係ないよ。これは自分用」
『自分用?』
「自分で TraceChars() を作る時に p_ch のアドレスを変えないように。
ポインタじゃなくて int を引数に取る時とかも結構するよ」
『ふーん』
「で、今回のもすべて、バグをコンパイルエラーで拾う方法」
『そいや、範囲の違う変数を使うとコンパイルエラーになるもんね』
「というわけで、次はコンパイルエラーじゃない方法で拾ってみましょう」
/*
Preview Next Story!
*/
『でも const int の引数って、使われてるの見たことないんだけど』
「使ってるの僕くらいかもしれないね」
『げ、そゆこと教える?』
「でも、いいことだとは思うでしょ?」
『まあそりゃぁ……』
「というわけで次回」
< Version 6.17 ASSERT の網 >
『につづく!』
「ま、使うか使わないかは火美ちゃんが決めて」
『じゃあ使わない』
「なにーっ!?」