Version 16.02
メンバ変数とメンバ関数
「前回は簡単にクラスを作ってみました」
『あとメンバ変数もね』
「今回はメンバ変数のおさらいと、さらにメンバ関数を追加します。
メンバ関数は、クラスが持つ関数です」
『メンバ変数の関数版ね』
「それではメンバ変数とメンバ関数を作ってみます。まず、ヘッダーファイル
の方に以下のようにメンバ変数とメンバ関数を追加します」
// Data.h
// CDataクラス。
class CData
{
public:
// メンバ変数。
int m_iValue;
// メンバ関数。
void Output();
};
「メンバ変数の作り方は、普通のローカル変数と同じ。〈型 メンバ変数名〉
で宣言できます」
『メンバ関数は〈{}〉がないから、関数の宣言部と同じね』
「そう、こちらも基本的には普通の関数の宣言と同じ」
『ってことは、関数の定義も書かなきゃいけないんだよね』
「そういうこと。というわけで、次に Data.cpp 、つまりソースファイルの
方を作ります」
『メニューの【ファイル】-【新規作成】選んで、【新規作成】ダイアログ
で【ファイル】
ページの【C++ ソースファイル】を選んで、【ファイル名】を Data.cpp に
するんだよね』
「そうそう」
『もう何度もやってるからいい加減できないとね。ファイルにはどう書く
の?』
「こんな感じ」
// Data.cpp
#include <Windows.h>
#include <stdio.h>
#include "Data.h"
// CDataクラスのOutput()メンバ関数の定義。
void CData::Output()
{
// 出力します。
char pch[256];
sprintf( pch, "%d\n", m_iValue );
OutputDebugString( pch );
// 100
}
「まず、 Data.h をインクルードします」
#include "Data.h"
「そうしないと CData クラスの情報が得られないからね」
『んでそのあとが Output() メンバ関数の定義ね』
// CDataクラスのOutput()メンバ関数の定義。
void CData::Output()
{
// 出力します。
char pch[256];
sprintf( pch, "%d\n", m_iValue );
OutputDebugString( pch );
// 100
}
「メンバ関数は〈戻り値の型 クラス名::メンバ関数名( 引数 ){実装}」と
いう形で定義します」
『ようするに、関数の前に〈クラス名::〉を付ければいいわけね』
「そういうこと。これ付けないと、ただの関数になるから注意して」
『そか、そういえばそうだね』
「このメンバ関数の中では、メンバ変数にアクセスすることができます」
sprintf( pch, "%d\n", m_iValue );
『 m_iValue がそのメンバ変数だよね』
「そう、 Data.h の方にあるメンバ変数。メンバ関数はメンバ変数に普通に
アクセスすることができます」
『外からもアクセスできるし、こうして中、メンバ関数からもアクセスでき
るわけね』
「そういうこと。そして、このメンバ関数も、外から呼び出すことができま
す」
// Main.cpp
#include <Windows.h>
#include <stdio.h>
#include "Data.h"
int WINAPI WinMain
( HINSTANCE p_hInstance
, HINSTANCE p_hPrevInstance
, LPSTR p_pchCmdLine
, int p_iCmdShow
)
{
// CDataクラスの変数を宣言します。
CData cData;
// m_iValueに値を入れます。
cData.m_iValue = 100;
// メンバ関数を呼び出します。
cData.Output();
return 0;
}
「まず、 CData クラスの変数を作ります」
// CDataクラスの変数を宣言します。
CData cData;
「メンバ変数には、〈クラス型変数.メンバ変数〉の形でアクセスすること
ができます」
// m_iValueに値を入れます。
cData.m_iValue = 100;
「メンバ変数 m_iValue は、 cData 変数の中に入っている変数、というこ
とになります」
『前回言ってたね、メンバ変数は変数の中にある、って』
「そう、だからメンバ変数が増えれば、 cData そのもののサイズも増えま
す。クラス型変数は、メンバ変数によって構成された変数、だから」
『メンバ変数で構成された変数?』
「そう。たとえば、今の状態だと sizeof( CData ) は、中のメンバ変数
m_iValue のサイズと同じ」
『 4 バイト?』
「になります。 m_iValue がない場合、 sizeof( CData ) は」
『…… 0 バイト?』
「にはさすがにならないんだけど、 1 バイトになります」
『あー、やっぱ減るんだ』
「それよりも、メンバ変数があるときは、メンバ変数のサイズと同じ、
っていう点が重要」
『まさに、メンバ変数がクラスを構成している、ってわけね』
「そういうこと。だから、クラス型変数は、メンバ変数だけで構成されてい
て、それがひとつの変数にくるまれてる、ってイメージかな」
『ちっこい変数に見えていっぱいある、ってことねなんか構造体みたい……
って、確か構造体とクラスって同じなんだっけ』
「 Version 7.07 ( No.127 ) で説明したね。そう、ほとんど同じもの。だ
からクラスでも同じイメージの方が分かりやすいかもね」
『うんうん』
「ただ、構造体が主にメンバ変数だけなのに対して、クラスでは」
『メンバ関数がある!』
「そういうこと。そのメンバ関数は〈クラス型変数.メンバ関数()〉の形で
呼び出すことができます」
// メンバ関数を呼び出します。
cData.Output();
「こうすることで CData クラスの Output() メンバ関数を呼び出すことが
できます」
『質問!』
「はい火美ちゃん」
『メンバ関数は、 cData の中に入ってないの?』
「入ってはいません」
『え、そなの?』
「 cData の中にメンバ関数が入っているわけではありません。実際、さっき
説明したように、サイズ的にもそうだし、もし持つとしても……関数を変数の
ように持つ方法は?」
『関数ポインタ?』
「そう、関数ポインタとして持つことになります。関数全体を変数に入れる
ことはできないわけだから」
『あ、そっか。そいやそうだね』
「そういう意味では、 Output() が cData に入っているわけではない、
メンバ関数がクラス型変数に入っているわけではない、というわけです」
『でもさ、だとしたら、 Output() メンバ関数って単独で呼び出せるんじゃ
ない? でも、クラス型変数使わなきゃ呼び出せないじゃん』
// クラス型変数を使わずに
// メンバ関数を呼び出します。
CData.Output();
// コンパイルエラー:
// error C2143: 構文エラー : ';' が '.' の前に必要です。
CData::Output();
// コンパイルエラー:
// 'CData::Output' :
// 静的でないメンバ関数の中で呼び出しが正しくありません。
「ほら、クラス型変数変数がないと呼び出せないじゃん」
「そう、それはなぜかというと、メンバ関数はメンバ変数にアクセスするた
めの情報が必要だから」
『メンバ変数にアクセスするための情報?』
「そう、 Output() メンバ関数が cData.m_iValue 変数にアクセスできな
きゃいけないわけだから」
『そのために、クラス型変数経由で呼ばなきゃいけない?』
「というわけで次回に続く!」
/*
Preview Next Story!
*/
『関数は中に入ってない、でもクラス型変数は必要』
「こういうところも、中の仕組みが解ると難しくないかも」
『その中の仕組みが難しいんだけど』
「そういうときは全体像を」
『以下ループ』
「というわけで次回」
< Version 16.03 this ポインタ >
『につづく!』
「そうして続けていくのがプログラミングの学習ってことで」
『いや、綺麗にまとめられても』