#pragma twice

KAB-studio > プログラミング > #pragma twice > 116 Version 6.16 気に掛ける範囲を狭める!

#pragma twice 116 Version 6.16 気に掛ける範囲を狭める!

前のページへ 表紙・目次へ 次のページへ

 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 の網 >
につづく!
ま、使うか使わないかは火美ちゃんが決めて
じゃあ使わない
なにーっ!?
 
del.icio.us 登録する
Yahoo!ブックマーク 詳細を表示 users
livedoorクリップ 詳細を表示 livedoorクリップ ブックマーク数
はてなブックマーク 詳細を表示 はてなブックマーク ブックマーク数
RSSに登録
del.icio.us 登録する
Yahoo!ブックマーク 詳細を表示 users
livedoorクリップ 詳細を表示 livedoorクリップ ブックマーク数
はてなブックマーク 詳細を表示 はてなブックマーク ブックマーク数
 
このページは、Visual C++ 6.0を用いた C++ 言語プログラミングの解説を行う#pragma twiceの一コンテンツです。
詳しい説明は#pragma twiceのトップページをご覧ください。