Version 11.16
簡単なクラスを作ってみよう!
「前回、 CString の文字列を操作するのには = や += が使えるって説明し
ました」
『ってゆーか、それって前に教わってるよね』
「そう、演算子のオーバーロードっていいます。 Version 5.22 ( No.087 )
や Version 7.09 ( No.129 ) で説明してるよね」
『ちょっと駆け足だったけど』
「というわけで、ここで簡単にクラスを作って、どういう仕組みで動いてる
のか試してみようと思います」
『……クラスを作る? 作れるの?』
「もちろん。そろそろ火美ちゃんも簡単なクラスは作れるようになった方が
いいから。簡単なものだけだけどね」
『いきなり難しいのなんて作れないし』
「もちろん。とりあえず簡単なのから。まずは、まったくなんの機能もない
クラス」
// 何もないクラス。
class CNon
{
};
『えらいシンプルね』
「見方としては」
class クラス名
{
// この中にクラスの機能が入ります。
};
「って感じ」
『クラス名って好きなのにしていいの?』
「他とかぶらなければね。あ、でも、一応最初に C ってつけた方がいいか
な。まぁ僕のスタイルだけど」
『 MFC はそうなってるよね』
「うん、それに慣れちゃってるからね。逆に、 MFC と区別するために C を
付けないいうのもありかな」
『なるほどね』
「次に、この中にメンバ変数を入れます」
// public なメンバ変数を持つクラス。
class CHasMember
{
public:
int m_iData;
};
「これは、こういう風に使えます」
void Use_CHasMember()
{
CHasMember cHasMember;
cHasMember.m_iData = 100;
TRACE( "%d\n", cHasMember.m_iData );
// 100
}
『なんか見た目が構造体っぽいね』
「というよりまさにそんな感じかな。 Version 7.07 ( No.127 ) でやった
でしょ」
『そっか、構造体とクラスってほとんど同じなんだもんね』
「だからこういう使い方もできるんだけど……実は、これは悪い例」
『え? だって構造体はこれでいいんでしょ?』
「構造体は昔の方式だから、仕方ないっていうか……。基本的に、変数へは
できる限り直接アクセスしない方がいいんです」
『直接じゃないってことは?』
「関数を通してアクセスするってこと。こんな感じに」
// メンバ関数を持つクラス。
class CHasMemberFunction
{
private:
int m_iData;
public:
// 値を返します。
int GetData() const
{
return m_iData;
}
// 値をセットします。
void SetData( int p_i )
{
m_iData = p_i;
}
};
「こんなふうに、 m_iData に値を取り出すするメンバ関数 GetData() と、
値をセットするメンバ関数 SetData() を用意して」
void Use_CHasMemberFunction()
{
CHasMemberFunction cHasMemberFunction;
cHasMemberFunction.SetData( 100 );
TRACE( "%d\n", cHasMemberFunction.GetData() );
// 100
}
「っていう感じに使います」
『なんかめんどくさい……これのメリットって?』
「まず、メンバ関数を挟む事でそこにブレークポイントを入れたり
TRACE() を入れたりできます」
『あ、そっか、変数直接だとそれはできないね』
「どこからアクセスされても、必ずこのメンバ関数は通るから、そこで待ち
かまえておけばどこから呼ばれたかもわかるしね。それに、値を変えたり
チェックしたりもできるから」
『チェック?』
「たとえばマイナスは不可にしたりとか」
『なるほど』
「間に何か挟む事で自由度が高くなるから。で、重要なことがもうひとつ。
private とか public って付いてるでしょ」
『うん、ついてる。つか、さっきは public だけで m_iData の前にあった
のに、今度はそれが private になってて、メンバ関数の前に public が付
いてる』
「そう、そこが重要。ちょっとこれをビルドしてみて」
void Use_CHasMemberFunction_Error()
{
CHasMemberFunction cHasMemberFunction;
cHasMemberFunction.m_iData = 100;
}
『げ、コンパイルエラー!』
error C2248: 'm_iData' : private メンバ
(クラス 'B1::CHasMemberFunction' で宣言されている)に
アクセスできません。
「実は、 private が付いてると、外からアクセスできなくなるんです。逆
に public が付いてると外からアクセスできるようになります」
『ってことは、さっき』
cHasMember.m_iData = 100;
『ができたのは、 public が付いてたからで、それができなくなったのは
private が付いてるから?』
「そういうこと」
『あれ? でもそれじゃ、 SetData() は外から呼べないはずじゃん』
「あ、 private や public はメンバ変数やメンバ関数ひとつひとつに対応
してるんじゃなくて、範囲で指定するんです。こんな感じに」
class CHasMemberFunction
{
private:
// この中のメンバは private になります。
// だから外からアクセスできません。
public:
// public が出てきたので private は無効に。
// この中のメンバは public になります。
// だから外からアクセスできます。
// public の範囲はここまで。
};
「っていうふうに、 private や public は、付けたら他のが出てくるまで
ずっと private だったり public だったりします」
『 public の範囲はここまで、ってあるってことは、クラスの中だけしか効
果がないのね』
「そういうこと。あと、何も付けてないと private になります」
class CHasMemberFunction
{
// ここは private 。
public:
// ここから public 。
// 以下略。
『ってことは最初の private っていらないんじゃない?』
「そうすることもできるんだけど、わかりにくいからつけた方がいいかも。
〈何も付いてない時には private 〉って憶えるのも面倒だし」
『確かにね』
「あともうひとつ。 GetData() のところ」
int GetData() const
「 const って付いてるでしょ」
『あ、ホントだ。何度か見た事あるけど……変数に付いてる const と同
じ?』
「意味はね。メンバ関数のところに const が付いていると、そのメンバ関
数ではメンバ変数を書き換えることができなくなります。たとえば」
int GetData() const
{
m_iData = 100;
return m_iData;
}
「ってすると」
『あ、コンパイルエラー』
error C2166: 左辺値は const オブジェクトに指定されています。
『左辺値?』
「まぁ、はっきりいって const メンバ関数はかなり難しい話になっちゃう
から、とりあえずは難しい事を考えないで〈値を返すメンバ関数には const
を付ける〉くらいでいいから」
『 GetData() はただ値を返すだけだから、付ける、と』
「そういうこと。で、今の CHasMemberFunction クラスが、クラスの一番基
本的なものだから、これくらいはすらっと書けるようになって欲しいかな」
『む、努力します……』
/*
Preview Next Story!
*/
『今までクラスなんて作ってなかったからむずかしー』
「ま、今はクラスを読むために作る、って感じかな」
『自分で作ってみれば解るようになるってこと?』
「そういうこと。どうしてそういう構造になってるかとか」
『理由がわかるから理解できる理由ね』
「この前使ったコンストラクタとデストラクタも」
『自分で作れば理解が深まる、と』
「というわけで次回」
< Version 11.17 コンストラクタとデストラクタを作る! >
『につづく!』
「ただあるものを使うよりよっぽど面白いと思うよ?」
『面白い、ねぇ……』