Version 16.22
演算子のオーバーロードの実例、の続き
「前回は、配列ポインタの代わりになる CIntPointer クラスを作成しまし
た」
『演算子のオーバーロード使いまくって、ポインタと同じような感じにする
んだよね』
「そう。 ++演算子、 !=演算子、 *演算子をオーバーロードして、ポインタ
と同じように使えるようにします。実際の使用例はこんな感じ」
// Main.cpp
#include <Windows.h>
#include <stdio.h>
#include "IntPointer.h"
int WINAPI WinMain
( HINSTANCE p_hInstance
, HINSTANCE p_hPrevInstance
, LPSTR p_pchCmdLine
, int p_iCmdShow
)
{
int iArray[5];
// forで全部セットします。
for
( CIntPointer cIntPointer = iArray
; cIntPointer != ( iArray + 5 )
; ++cIntPointer
)
{
*cIntPointer = 100;
}
// 配列の方を出力します。
char pch[256];
sprintf
( pch
, "%d, %d, %d, %d, %d\n"
, iArray[0]
, iArray[1]
, iArray[2]
, iArray[3]
, iArray[4]
);
OutputDebugString( pch );
// 100, 100, 100, 100, 100
return 0;
}
『うわ、本当にポインタと同じように使えてる!』
「それぞれの箇所を見てみます。まず for の初期化部分」
for
( CIntPointer cIntPointer = iArray
「ここでは、CIntPointerクラスの変数を宣言して、その時コンストラクタ
で int 型ポインタを受け取っています」
『コンストラクタって事は、これのことだね』
// コンストラクタ。
CIntPointer::CIntPointer( int *p_pi )
: m_pi( p_pi )
{
}
『なるほど、なんか普通に代入しているように見えて、もうここから色々
やってるのね』
「そう、引数1つのコンストラクタは =演算子で呼び出せる、というのを利
用しているわけです。次に、 for の条件部分」
; cIntPointer != ( iArray + 5 )
「ここでは、 !=演算子のオーバーロードメンバ関数を呼び出しています」
『これね』
// !=演算子。
BOOL CIntPointer::operator !=( int *p_pi )
{
if( p_pi == m_pi )
{
return FALSE;
}
return TRUE;
}
『ポインタを引数に受け取って、一致していたら FALSE 、そうじゃなかっ
たら TRUE を返す、ってわけね』
「そうすることで、ポインタの比較と同じことをしている、というわけ」
『あ、そっか、ポインタを真似てるんだもんね』
「そこが重要かな。さっきのコンストラクタもそうだけど、ポインタと同じ
ように使える、っていうことが重要だから」
『クラスと意識しなくても使えるってわけね』
「そういうこと。同じく、 ++演算子のオーバーロードも」
; ++cIntPointer
『これはこれね』
// ++演算子。
int * CIntPointer::operator ++()
{
return ++m_pi;
}
「こういった演算子のオーバーロードを行うことで、ポインタと同じように
扱うことができるわけ」
『ポインタ似のクラスになるってわけね。 for の中のこれもそうだよね』
*cIntPointer = 100;
『これは *演算子だからこれね』
// *演算子。
int &CIntPointer::operator *()
{
return *m_pi;
}
『これちょっと難しいかも』
「そうだね、前回のアドレスの図を使うと、 ++演算子のオーバーロード
メンバ関数を呼び出すごとに、 m_pi は以下のようにアドレスが増えて
いきます」
iArray iArray + 5
m_pi ++m_pi ++m_pi ++m_pi ++m_pi ++m_pi
↓ ↓ ↓ ↓ ↓ ↓
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
← → ← → ← → ← → ← →
iArray[0] iArray[1] iArray[2] iArray[3] iArray[4]
『うん、 operator ++() で ++m_pi してるからだよね』
「そうそう。で、その時その時の *m_pi は、 iArray の各要素と同じにな
るんです」
iArray iArray + 5
m_pi ++m_pi ++m_pi ++m_pi ++m_pi ++m_pi
↓ ↓ ↓ ↓ ↓ ↓
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
← → ← → ← → ← → ← →
iArray[0] iArray[1] iArray[2] iArray[3] iArray[4]
*m_pi *m_pi *m_pi *m_pi *m_pi
『そか、ポインタに * つけると、その位置の変数になるんだもんね』
「 Version 4.10 ( No.060 ) や Version 5.03 ( No.068 ) で説明したよう
に、 iArray[iF1] と *( iArray + iF1 ) 同じ。だから、 *m_pi はそれぞ
れの要素となります」
『で、…………?』
「 operator *() は、その参照を返しているでしょ。ということは、
iArray[0] や iArray[1] といった各要素の参照を返している、つまり」
*cIntPointer = 100;
↓↓↓↓↓↓↓
iArray[iF1] = 100;
「みたいな感じになるんです」
『あ、要素に代入!』
「このように、要素の参照を返すことで、要素そのものとして扱うことがで
きるわけです」
『はー、なんか複雑かも』
「まぁそうなんだけど、こういうことを一から考え出す必要はないし、
パターンのひとつと思った方がいいかも」
『そのパターンがいっぱいあると憶えきれないんだけど』
「確かに……話を戻すと、このように iArray の各要素の参照を返すこと
で、それぞれの要素に代入することができます。その結果を確認しているの
が以下の箇所」
// 配列の方を出力します。
char pch[256];
sprintf
( pch
, "%d, %d, %d, %d, %d\n"
, iArray[0]
, iArray[1]
, iArray[2]
, iArray[3]
, iArray[4]
);
OutputDebugString( pch );
// 100, 100, 100, 100, 100
「出力しているのは iArray の方だから間違えないで」
『そか、 CIntPointer を使っていたけど、ちゃんと iArray の方に値が入
れられてる、ってわけね』
「そういうこと。 CIntPointer は、前回の配列の例での、 pi の代わりだ
から」
『そのポインタの代わりになるクラス、ってわけね』
「重要なのは、ポインタに似せる、ということ。演算子のオーバーロードの
目的は、トリッキーなことをするためじゃなく、 int みたいな基本型と同
じことがクラスでできるようにすることだから」
『こんなふうに、ポインタと同じように使えるようにってことね』
「実際にはここまでちゃんとしたクラスを作ることはあまりないけど、使う
ことはあるかもしれないから、今回説明したところくらいは憶えておくとい
いかな」
/*
Preview Next Story!
*/
『水希ちゃん、テンプレートってなに?』
「それは #pragma twice では説明しません」
『ええーっ!? そのためのフリじゃなかったの?』
「テンプレート解説したらそれだけで半年かかるから」
『いーじゃんいまさら……』
「次回は引数のデフォルト値についてです」
『あー話逸らした!』
「というわけで次回」
< Version 16.23 引数のデフォルト値 >
『につづく!』
「さすがにもう長すぎるから、ね……」
『もう8年目に突入だもんね……』