Version 16.05
ごく一般的なクラス
「ここまでの説明を踏まえて、一般的なクラスについて説明します」
『一般的なクラス?』
「そう、クラスとして必要なものを説明します。あ、基本的には
Version 11.16 ( No.216 ) の復習になるので、その回をひととおり見てお
くのもいいかも」
『あ、そういえばあの時も簡単なクラスって説明してもらったね』
「今回はおさらいに加えて詳しい説明もします」
『おー』
「まず、基本は【privateメンバ変数】とその【getter】【setter】です」
『げったーとせったー?』
「そう。カタカナで【ゲッター】【セッター】と言ったり、合わせて
【アクセサー】と言ったりもします。これらは、【privateメンバ変数】に
アクセスするためのメンバ関数です」
// Data.h
// CDataクラス。
class CData
{
private:
// private メンバ変数。
int m_iData;
public:
// m_iData の getter 。
int GetData() const;
// m_iData の setter 。
void SetData( int p_iData );
};
「まず、クラスのメンバ変数は、 private にします」
private:
// private メンバ変数。
int m_iData;
「これは、特別な場合を除いて、全てのメンバ変数は private にすると思
ってください」
『全部? 基本的に public メンバ変数って使わない?』
「一部の例外を除いて使いません。前に【グローバル変数】について説明し
たと思うけど、そのグローバル変数の問題点って憶えてる?」
『えと、使える範囲が広いから、だっけ。 Version 6.16 ( No.116 ) でそ
う教わったけど』
「そう、グローバル変数は〈どこからでも〉アクセスできるというメリット
がある反面、アクセスされる範囲をコントロールできないというデメリット
があります」
『把握しきれなくなるんだよね』
「変数は、関数のように〈デバッグ時にアクセスを調べる〉ことができませ
ん。なので、変数がどこからアクセスされているか、ということを常に
プログラマーが把握していないと、思わぬタイミングで思わぬ値に書き換え
られていた、ということになりかねません」
『注意してればなんとかなるかな、って気もするけど』
「仕事が忙しくなると注意しきれないし、変数そのものだけじゃなくて、
ポインタや参照まで絡んでくると」
『げげ、そういえばそうだね、すごく範囲広くなるよね』
「アクセスできる範囲が広い、ということはそれだけ危険というわけです」
『だから、範囲を狭くして、アクセスできる関数を限定するわけね』
「そういうこと。メンバ変数が public か private か、っていうのも同じ」
『そだね、 public だと他のクラスとか関数からもアクセスされるけど、
private なら同じクラスからだけ、って決まってるから大丈夫だもんね』
「でも、それだとメンバ変数から値を取得したり、メンバ変数に値をセット
したりといったことができません。そこで、そのメンバ変数にアクセスする
ためのメンバ関数、 getter と setter を用意します」
public:
// m_iData の getter 。
int GetData() const;
// m_iData の setter 。
void SetData( int p_iData );
「この getter と setter は、以下のルールの関数にします」
・getter
メンバ変数の型 Getメンバ変数名() const;
・setter
void Setメンバ変数名( メンバ変数の型 引数名 );
『これって固定?』
「基本的に固定。なぜ固定かというと、こうすれば【ブロック選択】や
【正規表現】を使って簡単に作ることができるから」
『ブロック選択とかって、 Version 13.26 ( No.262 ) で教わった便利なや
つだよね』
「そう、これを使ってメンバ変数から簡単にアクセサを作ることもできるか
ら、ルールを決めて統一しておくと便利。分かりやすいしね」
『関数名は、メンバ変数の名前から付けるんだよね。でもそのままじゃない
ね』
「そうだね、 m_i とかの、型とかを意味する部分は取り除いたメンバ変数
名がいいかな」
『だから GetData() と SetData() ね』
「そういうこと。で、これらのメンバ関数の定義はこんな感じになります」
// Data.cpp
#include <Windows.h>
#include <stdio.h>
#include "Data.h"
// m_iData の getter 。
int CData::GetData() const
{
return m_iData;
}
// m_iData の setter 。
void CData::SetData( int p_iData )
{
m_iData = p_iData;
}
「まず、 getter は値を取得します」
// m_iData の getter 。
int CData::GetData() const
{
return m_iData;
}
「このように、メンバ変数の値を返すのが getter 」
『フツーに返してるね』
「対して、値をセットするのが setter 」
// m_iData の setter 。
void CData::SetData( int p_iData )
{
m_iData = p_iData;
}
「引数から渡された値をメンバ変数にセットします」
『これも普通ねー。……なんか、これだけだとメンバ変数を public にして
もいいかもって思うんだけど』
「そう思うのはもっともだけどね。関数作らなきゃいけないから。でも、そ
れでも作った方がいいよ」
『やっぱ、アクセスが分かるから?』
「そう、ブレークポイントを置いたり、 OutputDebugString() や TRACE()
を置いたりできるし、その時のメンバ変数を出力できたりもするからね。そ
れに、値の修正もできるし」
『値の修正?』
「 setter や getter のメリットのひとつに、値を修正することもできる、
というものがあります。たとえば渡された値を 100 以下にする、とか」
『あ、 setter の中でそう処理する、ってわけね』
「これは結構重要で、たとえばビットフラグを使う場合、フラグに使える
数って決まってるでしょ」
『普通 #define や const int 使って決めるよね』
「メンバ変数でこのフラグを格納する場合、自由にアクセスできると好きな
値が入れられちゃう、つまりフラグとは関係ない値を入れてしまう可能性が
あるわけ」
『そか、 setter でそれを監視できる!』
「そういうこと。つまり」
CData クラス | CData クラスの外
|
[private の壁] |
| |
←←←←←←←←←←←←←←SetData()←←←←値をセット
↓ | |
m_iData | |
↓ | |
→→→→→→→→→→→→→→GetData()→→→→値を取得
| |
| |
「という感じに、 m_iData メンバ変数を CData クラスの中に囲い込み、外
との接点は public メソッドである setter と getter が受け持つようにす
るわけです」
『 m_iData は箱入り娘って感じね』
「こうすることで、 m_iData メンバ変数を外界から守り、適切な状態に保
つことができます」
『適切な状態に保つ?』
「さっきのビットフラグの例なら、正しいビットフラグが入っているように
したり、文字列なら NULL が入っていないようにする、整数値なら正しい範
囲の数が入っているようにする、といった状態にすること」
『なるほど、この時はこの値じゃなきゃいけない、とかあるもんね』
「そういうことを守るために外と中を分けるわけです。これを【カプセル化】
と言います」
『かぷせるか?』
「そう。カプセルって」
『あのガシャポンみたいなのだよね』
「……うん、まぁそう。ようするに中のものを梱包するための容器のこと。
カプセル化は、メンバ変数を private にして、外からアクセスできなくす
ることを言います」
『あ、じゃあ本当に箱入り娘なんだね』
「こういったルールを守るためにも、メンバ変数おアクセスを制限する必要
があるので、メンバ変数は private にして、アクセスは getter と setter
に限定するわけです」
/*
Preview Next Story!
*/
『なんかほとんど同じことのはずなのに不思議と新鮮』
「憶えてないだけとか」
『ぶー。そういえば、前の時とちょっとプログラムの書き方違くない?』
「そういえばそうだね。これは触れておいた方がいいかな」
『げ、やぶ蛇?』
「まぁいつか説明するつもりだったし」
『というわけで次回』
< Version 16.06 インラインメンバ関数 >
「につづく!」
『サブタイがエロい件について』
「いや、全然そういうんじゃないから」