Version 11.20
演算子をオーバーロードしよう
「今回は演算子をオーバーロードしてみます」
『演算子のオーバーロードって、クラスで演算子使うためのだよね』
「そう、 Version 7.09 ( No.129 ) でやったね。今回はこれを作ってみま
す。まずは基本的な例から」
// 演算子のオーバーロード。
class CHasOperator
{
private:
int m_i;
public:
// 演算子のオーバーロード。
int operator = ( int p_i )
{
m_i = p_i;
TRACE( "= %d\n", m_i );
return m_i;
}
};
『やっぱなんか見慣れない……』
「使い方はこう」
// 使用例。
void Use_CHasOperator()
{
CHasOperator cHasOperator;
cHasOperator = 100;
// = 100
}
『 = でメンバ関数呼べるようになる、ってことだよね』
「そう。オーバーロードの構文はこんな感じ。
戻り値 operator 演算子 ( 引数 )
{
// メンバ関数の本体。
}
「結構自由度は高いから。戻り値や引数は好きなものにできるよ」
『今回は戻り値が int 、引数も int ね。戻り値って必要?』
「場合によるかな。たとえば、普通の int の場合」
int i1;
int i2;
i1 = i2 = 100;
「っていうふうに〈他に代入したのをまた他に代入〉っていうことができる
んです。戻り値を int にしておけば、同じように」
CHasOperator cHasOperator1;
CHasOperator cHasOperator2;
cHasOperator2 = cHasOperator1 = 100;
// = 100
// = 100
「っていうことができるんです」
『こうすればより演算子に似るわけね』
「言い換えると、演算子に似た使い方ができるようにするには、色々とコツ
が必要なんです」
『確かに、これは知らないとできないね』
「それと、演算子にできるだけ似せることがコツのひとつとも言えるかな」
『どゆこと?』
「たとえば」
// 演算子のオーバーロード。
int operator = ( int p_i )
{
m_i -= p_i; // <ここ。
TRACE( "= %d\n", m_i );
return m_i;
}
『あ、 -= になってる』
「演算子のオーバーロードをすると、こういうこともできちゃうんだけど、
こういうことをすると混乱の元だからやめた方がいいかな」
『 = だったら中に代入するだけ、って感じに演算子に似せるわけね』
「そゆこと。あと、引数の数に関しては制約があって、こっちはちょっと難
しいかも。 Version 6.09 ( No.109 ) で【単項演算子】や【二項演算子】
ってやったでしょ」
『演算子にくっつける変数の数だね。あ! そっか、それが引数の数になる
んだ』
「そういうこと。オペランドのひとつは自分自身になるから、 1 引いた数
が引数の数ってことだね。たとえば、 ++ は単項演算子だからオペランドは
ひとつ」
『で、自クラスの 1 をひくと……ゼロだね』
「だから、引数はいらなくなります」
// 演算子のオーバーロード。
class CHasOperator_Increment
{
private:
int m_i;
public:
// コンストラクタ。
CHasOperator_Increment()
{
m_i = 0;
}
// ++ 演算子のオーバーロード。
int operator ++()
{
++m_i;
TRACE( "++ %d\n", m_i );
return m_i;
}
};
「使用例はこんな感じ」
void Use_CHasOperator_Increment()
{
CHasOperator_Increment cHasOperator;
++cHasOperator;
// ++ 1
}
『ってことは、演算子ごとにそういうの考えなきゃいけないの?? めんど
くさー』
「さらにめんどくさいことに、 ++ って ++i っていう形式と i++ っていう
形式とふたつあったでしょ」
『 ++i が先に 1 増やして、 i++ があとで 1 増やすんだよね』
「そう、 Version 2.7 ( N0.018 ) でやったね。このふたつは区別されて
て、 i++ の方を作りたい場合には int の引数ひとつを入れることになって
ます」
// 後置 ++ 演算子のオーバーロード。
int operator ++ ( int p_i )
{
++m_i;
TRACE( "++ %d\n", m_i );
return m_i;
}
『なんかややこしいね……』
「あらかじめ言っておくと、これからさらにややこしくなるから」
『げ!』
「なぜかっていうと、元々あった仕様に無理矢理追加したから」
『演算子のオーバーロードってあとから付け加えたものってこと?』
「そういうこと。だから色々と矛盾とか問題とかあるんです」
『……使わない方がよくない?』
「微妙だね。メリットも多いから使った方がいいけど、他の言語では採用さ
れてないから、むしろデメリットの方が多いって考え方もあるかも」
『うーん……』
「でも、 C++ やる上では必要だから」
『う”、つまり私は勉強しなきゃいけないってこと?』
「そゆこと」
『うー』
「最後に、型変換をしてくれるオーバーロードを紹介します」
『型変換?』
「 Version 5.22 ( No.087 ) や Version 7.09 ( No.129 ) で紹介した、自
動的に他の型にくれる機能のこと」
『あー、 CString を LPCTSTR に、とかね』
「そうそれ。というわけでその例がこれ」
// 演算子のオーバーロード。
class CHasOperator_Return
{
private:
int m_i;
public:
CHasOperator_Return()
{
m_i = 0;
}
// 型変換演算子のオーバーロード。
operator int()
{
TRACE( "(int)%d\n", m_i );
return m_i;
}
};
「使用例はこんな感じ」
void Use_CHasOperator_Return()
{
CHasOperator_Return cHasOperator;
int i = cHasOperator;
TRACE( "%d\n", i );
// (int)0
// 0
}
『おー、 int に渡せば operator int() が呼ばれるってわけね。でもこの
関数、戻り値なくて変……』
「戻り値は決まってるから必要ないって事みたいだね」
『決まってる? あ、そっか、 int に渡すんだから int なんだ。……あ
れ? でもさ、オーバーロードって、戻り値で選べないんじゃないの?』
「うん、だからこれは特別。書き方も使い方も特別って感じかな」
『なんか変なの。便利だけど』
/*
Preview Next Story!
*/
『なんか、今までと勝手が違うかも』
「便利だけど危険、とか?」
『そうそう。教わってそれ使うだけでいい、じゃダメなの?』
「それじゃ、教わったことしかできないでしょ」
『うー』
「プログラミング言語の裏にある本質を見極めなきゃ」
『どっかの格闘バカみたい』
「というわけで次回」
< Version 11.21 「演算子のオーバーロード」の意味 >
『につづく!』
「次回もそういうむつかしい話」
『タイトルが微妙に哲学っぽい感じ……』