Version 16.27
static と this
「前回は static メンバ関数について説明しました」
『 static メンバ変数と static メンバ関数、どっちもクラスの変数を作ら
なくても使うことができるんだよね』
「そう、それが普通のメンバ変数、メンバ関数との違い」
『でも、 static な方から普通の方って使えないんだよね』
・static メンバ関数→→→×→→→ 普通のメンバ変数、 普通のメンバ関数
・ 普通のメンバ関数→→→○→→→ static メンバ変数、static メンバ関数
『これがちょっと不便だよね』
「でも、ちゃんとした理由があるんです。なぜこうなるのか、まず仕組みで
考えてみます」
『はーい』
「まず、普通のメンバ関数は置いといて、普通のメンバ変数に絞って考えた
方が分かりやすいかな」
『普通なメンバ変数、つまり非 static メンバ変数ってことね』
「普通のメンバ変数やメンバ関数を使うためには、クラスの変数が必要で
す。特にメンバ変数の方は、クラスの変数の中に置かれる形になるから」
『 Version 16.02 ( No.329 ) で言ってたね、メンバ変数がそのクラスの
変数の中身になる、ってことなんだよね』
「そういうこと。だから、普通のメンバ変数はそのクラスの変数が必要不可
欠です。さて、この普通のメンバ変数を、 static メンバ関数から使用する
場合を考えてみます」
『 static メンバ関数は、クラスの変数がなくても呼び出せる……そか、
static メンバ関数から普通のメンバ変数にアクセスするためには、その
メンバ変数が入っている、クラスの変数が必要、でも』
「 static メンバ関数を呼び出す時にははクラスの変数がないわけだから」
『そのメンバ変数が入ってるクラスの変数が分からない! ってこと?』
「そういうこと。 static メンバ関数は、クラスの変数とは関係なく呼び出
せるわけだから、そのクラスの変数に入っているメンバ変数とも関係ない
わけです」
『なるほど……んー、でももうちょっと欲しいかも、なんかが』
「じゃあ別の視点から考えてみようか。 this ポインタって憶えてる?」
『 Version 16.03 ( No.330 ) で教わったね、自分の変数のポインタで、隠
された引数……あ!! そっか、 static メンバ関数って、 this ポインタ
がないんだ!』
「そう! static メンバ関数は、クラスの変数を持たずに呼び出すから、
普通のメンバ関数が【呼び出したときに後ろで渡される、クラスの変数の
ポインタ】つまり this ポインタが渡されないんです」
『メンバ関数からメンバ変数使う時は、 this->m_iData みたいに this
ポインタ使うのと同じ、ってことだったから、その this ポインタがない
static メンバ関数は、メンバ変数にアクセスできない!』
「というわけです。これをプログラムで確認してみようか。 CData クラス
に、普通のメンバ変数 m_iNonStaticMember 、普通のメンバ関数
NonStaticMember() 、 static メンバ関数 StaticMember() を持たせます」
// Data.h
// CDataクラス。
class CData
{
public:
// 非 static メンバ変数。
int m_iNonStaticMember;
// static メンバ関数。
static void StaticMember();
// 非 static メンバ関数。
void NonStaticMember();
};
// Data.cpp
#include <Windows.h>
#include <stdio.h>
#include "Data.h"
// static メンバ関数。
void CData::StaticMember()
{
// this->m_iNonStaticMember = 100;
}
// 非 static メンバ関数。
void CData::NonStaticMember()
{
this->m_iNonStaticMember = 100;
}
// 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
)
{
// static メンバ関数を使用します。
CData::StaticMember();
// 非 static メンバ関数を使用します。
CData cData;
cData.NonStaticMember();
return 0;
}
「 NonStaticMember() メンバ関数は、 cData のアドレスを this ポインタ
として受け取ります」
→→→→→→→→→→↓
↑ ↓
cData.NonStaticMember(↓);
↓
→↓
↓
void CData::NonStaticMember(↓)
{ ↓
←←←←←←←←←←←
↓
this->m_iNonStaticMember = 100;
}
「 NonStaticMember() メンバ関数は、 cData のアドレスを this ポインタ
として受け取るため、 this ポインタを通して普通のメンバ変数やメンバ関数
を使うことができるわけです」
『でも、 StaticMember() メンバ関数は、この this ポインタを受け取らな
い』
「クラスの変数を作っていないから、渡しようがないんです」
// static メンバ関数を使用します。
CData::StaticMember();
// static メンバ関数。
void CData::StaticMember()
{
// this ポインタがないから非 static メンバ変数には
// アクセスできません。
// this->m_iNonStaticMember = 100;
}
「 StaticMember() メンバ関数を呼び出す時に CData クラスの変数を必要
としないので、そのアドレスを持つ this ポインタも渡されないわけです」
『普通のメンバ変数を使うためには this ポインタが必要だから、アクセス
できないってわけね』
「そういうこと」
『……んー、普通のメンバ変数はこれでわかったけど、普通のメンバ関数を
static メンバ関数から呼び出せないのってなんで?』
「同じように考えればわかると思うよ。普通のメンバ関数を呼び出すときに
裏で渡されるのは?」
『クラスの変数のアドレスが入った this ポインタ』
「それを static メンバ関数は」
『持ってない!』
「そういうこと」
// static メンバ関数。
void CData::StaticMember()
{
// 非 static メンバ関数を呼びたいけど呼べません。
// 渡さなきゃいけない this ポインタがないから。
// NonStaticMember( this );
}
「普通のメンバ関数は、中で普通のメンバ変数にアクセスするために this
ポインタが必要になるので、必ず渡す必要があります」
『でも static メンバ関数はそれを持ってない、渡せない、だから呼び出せ
ない』
「そういうこと。こんなふうに、 this を中心に考えると分かりやすいか
な」
『うん、なんとなく分かったかも』
「もう一度まとめると、以下のようになります」
・static メンバ関数→→→×→→→ 普通のメンバ変数、 普通のメンバ関数
・ 普通のメンバ関数→→→○→→→ static メンバ変数、static メンバ関数
『つまり static メンバ関数から普通のメンバは使えない、 this ポインタ
がないからアクセスできないってわけね』
「そういうこと」
『でもさ、それってすごく不便じゃない? 普通にクラスの変数作って普通
にメンバ関数呼び出せばいいんだから、 static メンバ関数とか作る意味な
いじゃん』
「でも実は使い道があるんです。というわけで次回に続く!」
/*
Preview Next Story!
*/
『不便! 使いづらい! 不便!』
「でもWin32 APIを使う時には便利なんです」
『 static メンバ変数が?』
「ううん、 static メンバ関数が」
『というわけで次回』
< Version 16.28 関数の代わりに static メンバ関数 >
「につづく!」
『 static メンバ変数は?』
「えーっと……」