Version 7.09
演算子のオーバーロード
「えーっと、今日は難しいからちょっと気を抜いててもいいよ」
『なにぃ!? なんか矛盾してるってそれ』
「コンストラクタもそうだけど、〈使う側〉の場合はそんなに厳密に理解し
てなくても使えるわけだから」
『っつーことは、使う段階になったら真面目に憶えなきゃいけないわけね』
「そゆこと。で、今回は前回の続き」
void CAnimeDlg::OnBDraw()
{
CRect cRect;
::GetWindowRect( GetSafeHwnd(), cRect );
TRACE
( "%d, %d, %d, %d\n"
, cRect.left, cRect.top
, cRect.right, cRect.bottom );
}
『そうそう、ホントは &cRect じゃなきゃいけないはずなのに、 & が付い
てないのよねー』
「あ、ちなみに」
void CAnimeDlg::OnBDraw()
{
RECT stRect;
::GetWindowRect( GetSafeHwnd(), stRect );
// error C2664: 'GetWindowRect' :
// 2 番目の引数を 'struct tagRECT' から 'struct tagRECT *' に
// 変換できません。
}
『あ、ビルドでエラーになった』
「つまり RECT 構造体にはこの機能はありません。 CRect の機能だから」
『コンストラクタみたく、 CRect にそういう機能が付けられてるわけね』
「で、この仕組みを見る前に、もうひとつ、別の機能も見て」
void CAnimeDlg::OnBDraw()
{
CRect cRect1;
::GetWindowRect( GetSafeHwnd(), &cRect1 );
CRect cRect2;
::GetWindowRect( GetSafeHwnd(), &cRect2 );
if( cRect1 == cRect2 )
{
TRACE( "cRect1 == cRect2\n" );
}
// cRect1 == cRect2
}
『ん? 同じだよね、 cRect1 と cRect2 って』
「だから一致します」
『なるほど。で?』
「このコードのポイントは、 cRect1 == cRect2 。実は、クラスや構造体は
== で普通に比較できないんです」
『え、そうなの?』
「やってみようか。 RECT の場合」
void CAnimeDlg::OnBDraw()
{
RECT stRect1;
::GetWindowRect( GetSafeHwnd(), &stRect1 );
RECT stRect2;
::GetWindowRect( GetSafeHwnd(), &stRect2 );
if( stRect1 == stRect2 )
{
TRACE( "stRect1 == stRect2\n" );
}
// error C2678: 二項演算子 '==' : 型 'struct tagRECT' の左
// オペランドを扱う演算子は定義されていません。
// (または変換できません)
}
『ホントだ、エラーになった』
「構造体やクラスで比較をする場合には、メンバ変数ひとつずつ比較しなき
ゃダメ。 == だけじゃなく、 + とかの四則演算系とかもそう」
『面倒ねー。あ、だから CRect にはそういう機能がある!』
「そのとおり! まずは MSDN の方を見てみようか」
『ほい、 CRect のメンバ関数一覧っと』
「下の方に〈演算子〉ってあるでしょ」
『ある。 operator LPCRECT とか operator == とか。これが?』
「そうこれが。まず、ここに書いてある【 operator 】は無視して読んで」
『無視すると、演算子がずらーり』
「その演算子は、このクラスに使えるって事」
『だから == を使えるってことね』
「演算子じゃないの、たとえば LPCRECT とかは、この型には自動的にキャ
ストできるってこと」
『あ! これがあるから & 付けなくていいんだ。 LPCRECT って、 RECT の
ポインタだよね』
「そう、 const 版。 非 const なのは LPRECT の方」
『それもちゃんとあるね const が渡すの専用で、 const 付いてないのが受
け取り用、だって』
「 Ver 4.15 ( No.065 ) でやったね。 GetWindowRect() は」
『 LPRECT が引数になってるから LPRECT の方が呼ばれるんだね』
「ブレークポイント置いてステップインすれば確認できるから」
『え……ってことは、これってメンバ関数なの?』
「そう。 operator == のページを見てみて」
『ほい。ホントだ、引数が const RECT& で、戻り値が BOOL だ』
「ちなみに引数が RECT の参照だから」
『ってことは……』
void CAnimeDlg::OnBDraw()
{
CRect cRect1;
::GetWindowRect( GetSafeHwnd(), &cRect1 );
RECT stRect2;
::GetWindowRect( GetSafeHwnd(), &stRect2 );
if( cRect1 == stRect2 )
{
TRACE( "cRect1 == stRect2\n" );
}
}
『うお通った!』
「ちなみに参照については Ver 4.14 ( No.064 ) の見てください」
『あれ? じゃぁ……』
void CAnimeDlg::OnBDraw()
{
RECT stRect1;
::GetWindowRect( GetSafeHwnd(), &stRect1 );
CRect cRect2;
::GetWindowRect( GetSafeHwnd(), &cRect2 );
if( stRect1 == cRect2 )
{
TRACE( "stRect1 == cRect2\n" );
}
// error C2678:(以下略)
}
『げ、エラー!』
「そう、二項演算子って憶えてる?」
『 Ver 6.10 ( No.110 ) でやったね。演算子の左と右に変数取るのだね』
「そうそう。 cRect1 == cRect2 ってのも二項演算子だけど、この二項演算
子をメンバ関数として持つ場合、左オペランドのメンバ関数として呼ばれる
から」
『ってことは、 cRect1 のメンバ関数として呼ばれて……右オペランドの
cRect2 が引数として渡される!』
「そう! 上のでエラーが出ちゃうのは」
『 RECT には == のメンバ関数ないから』
「ってこと。このルールさえ憶えておけば、あとはメンバ関数と同じかな。
あ、ちなみに」
if( cRect1.operator ==( stRect2 ) )
『な、なにこれ?』
「 cRect1 == cRect2 を、メンバ関数っぽく呼んだ例」
『ちゃんとコンパイル通る……』
「というわけで、メンバ関数になってる部分を見てみましょう」
( Afxwin1.inl より抜粋)
_AFXWIN_INLINE BOOL CRect::operator==(const RECT& rect) const
{ return ::EqualRect(this, &rect); }
(抜粋ここまで)
『な、なんかよくわかんない……』
「うん、だから〈メンバ関数っぽい〉ってとこだけ解ってもらえればいいか
ら」
『あ、気を抜いていいんだもんね』
「そゆこと。とりあえずポイントは operator 。 == とかの演算子をクラス
のメンバ関数にする場合には、必ずこれを前に付けます」
『……それだけ聞くと簡単そうに聞こえる……』
「ま、上のコードは余分な情報が多いから解りにくいんだと思うよ。今回関
わってくる部分は operator だけだから」
『この部分はそれほど難しくないってことね』
「で、こういう〈演算子をクラスに使えるようにする機能〉のことを、
【演算子のオーバーロード】って言います」
『うわ、なんか難しそうな用語が!』
「ま、これは今はこのまんま憶えてもらっちゃってもいいや。
【オーバーロード】ってものについては今度別にちゃんと教えるから」
『げ、なんかまだ他に難しい事が?』
「だから難しいんだってば」
『難しかったり簡単だったり……』
「ちなみにこの演算子のオーバーロードは CString にもあったりします」
『うん、前にやったね。 operator LPCTSTR とか』
「 Ver 5.22 ( No.087 ) だね。でも、実際にはこの演算子のオーバーロー
ドがされてるクラスって多くないんだよね」
『そういえば、他の MFC クラスを見てもないね。便利じゃないのねー』
「でも、そうとも言えないんだよね」
『どゆこと?』
「たとえば operator LPCRECT 。これを使うと CRect をそのまま渡せ
ちゃうでしょ。ポインタとしてじゃなくて」
『うん、 & 付けないでね』
「こういうのってちょっと危なっかしいんだよね。もしポインタとかアドレ
スとかちゃんと知らないでこういうのを使っちゃうと」
『クラスだとそのまま渡せちゃう、とか思っちゃいそうだね』
「あと CString だと operator + () とかあるから、これで文字列をくっつ
けられるんだけど」
『これも普通の文字配列ならできないもんね、ポインタどーしだから』
「演算子のオーバーロードは、使ってるとこが〈目に見えない〉、つまりい
つの間にか使っちゃってることが多いんだよね」
『だからむやみにいろんなクラスに着いてたりはしないんだ』
「本来は初心者向けの機能なんだろうけど、むしろこれは、ちゃんと理解し
てる上級者向けの機能なのかもね」
/*
Preview Next Story!
*/
『難しかったり難しくなかったり』
「うん、それは言えるかも。たとえば _AFXWIN_INLINE とか」
『知らないもの……だけど、全部大文字だから、マクロ?』
「そ。これは調べれば解ること。でも CRect::operator==() const の」
『 const って何……なんでこんなとこに付いてるの……』
「これなんかは調べるのも難しいもののひとつかも」
『というわけで次回』
< Version 7.10 いろんな描画モード >
「につづく!」
『で、この const はなんなの?』
「これはメンバ変数の書き換えを防ぐもので……続ける?」
『その調べるのも難しいことをすらっと言う水希ちゃんて……』