【C/C++】 演算子のオーバーロード
最近C++でやたら演算子のオーバーロードをする事が多くて色々調べたり実験したりしていたんやけど、ここいらで覚書きをしておこう。
演算子をオーバーロードする目的には色々あるけど、主にオブジェクトに対して以下のような記述方法を使いたい時が上げられるかな?
obj A;
obj B;
obj C;
A = B;
A += B;
A = B + C;
もちろん + 以外にも -(sub) *(mul) /(div) &(and) |(or) ^(xor) <<(lshift) >>(rshift) などなど色々あるわけでさ。
それからコピーコンストラクタにポインタ参照、キャスト演算子などキリがないね。
で、↑のようなオブジェクト同士の演算であれば結構簡単。
class obj {
public:
void operator = ( obj o );
obj& operator + ( const obj& o );
obj& operaotr += ( const obj& o );
};
こんな感じ。
明示的に整数型を引数にとりたければ(つまり A += 1; などと記述したければ)
obj& operator +=( int i );
で対応できる。もしくは
class obj {
obj( int i );
obj& operator += ( const obj& o );
};
でも可能。その代わり整数の1を記録するためのテンポラリオブジェクトが生成されてしまうけどね。
さてさて、この辺の関数、全て obj& を戻り値としていたけど、どうやって値を返すのか?
これも結構簡単。
class obj {
obj& operator += ( const obj& o ) {
// なんらかの処理
return *this; // 自分自身を返す
}
};
とまぁ、*thisを返却すれば自分自身が返ります。
もちろん他のオブジェクトを返しても全く問題ないのやけど、ローカルオブジェクトだけは返却しないようにね。
一応一通りの演算子オーバーロード定義を書いておこう。
ちなみに対象は自身のオブジェクト型として、戻り型も自分自身とした場合です。
戻り型は必要に応じて変更してくださいな。
operator int( void ) { return i; } // キャストオーバーロード int a = (int)obj; 他のキャスト型も可能
obj& operator = ( const obj& b ); // 代入 a = b;
void operator ()( void ); // ファンクタ(関数オブジェクト)
obj& operator - ( void ); // 符号反転 -a
// 比較
bool operator ==( const obj& b ); // 比較 if ( a == b ) ...
bool operator !=( const obj& b ); // 比較 if ( a != b ) ...
bool operator <( const obj& b ); // 比較 if ( a < b ) ...
bool operator <=( const obj& b ); // 比較 if ( a <= b ) ...
bool operator >( const obj& b ); // 比較 if ( a > b ) ...
bool operator >=( const obj& b ); // 比較 if ( a >= b ) ...
// 単項演算
obj& operator += ( const obj& b ); // 加算 a += b;
obj& operator -= ( const obj& b ); // 減算 a -= b;
obj& operator *= ( const obj& b ); // 乗算 a *= b;
obj& operator /= ( const obj& b ); // 除算 a /= b;
obj& operator <<=( const obj& b ); // シフト a <<= b;
obj& operator >>=( const obj& b ); // シフト a >>= b;
obj& operator ++( void ); // 加算 ++a;
obj& operator ++( int ); // 加算 a++;
obj& operator --( void ); // 加算 --a;
obj& operator --( int ); // 加算 a--;
obj& operator &= ( const obj& b ); // AND a &= b;
obj& operator |= ( const obj& b ); // OR a |= b;
obj& operator ^= ( const obj& b ); // XOR a ^= b;
// 2項演算
obj& operator + ( const obj& b ); // 加算 a + b;
obj& operator - ( const obj& b ); // 減算 a - b;
obj& operator * ( const obj& b ); // 乗算 a * b;
obj& operator / ( const obj& b ); // 除算 a / b;
obj& operator <<( const obj& b ); // シフト a << b;
obj& operator >>( const obj& b ); // シフト a >> b;
obj& operator & ( const obj& b ); // AND a & b;
obj& operator | ( const obj& b ); // OR a | b;
obj& operator ^ ( const obj& b ); // XOR a ^ b;
さてと、ここからが本題。
上の定義を使うとオブジェクトに対するオペレータのオーバーロードが可能になる。
つまり
A = B + 1
とかがオブジェクトに対して行えるわけ。
この場合の処理順序は
- オブジェクトBに対して operator + (int i) 呼び出し
- オブジェクトAに対して operator =( const obj& b ) 呼び出し
になる。
ここで問題が起きる。もし上式を
A = 1 + B
と記述した場合、どうなるだろう?
この場合、1に対して + 演算子が処理される。でも 1 は組み込み型の int と解釈され、オブジェクトの演算子オーバーロードは影響しない。こういう時は項を入れ替えれば上の式と同じになるので、オブジェクトのオーバーロード関数で処理できるのやけど...。
で、こんな時のためにあるのがグローバル演算子のオーバーロード。
今までのは全てクラスに定義しているため処理対象はオブジェクトという事になっていたけど、グローバル演算子をオーバーロードしてしまえば組み込み型だろうが関係ないわけですな。
よって、
A = 1 + B
を処理するためには
obj& operator + ( int i, obj& b );
というグローバル演算子のオーバーロード関数を記述すれば良い。
こちらの関数はクラスの演算子オーバーロード関数と異なり、2項をとる。
上の式だと「整数 + オブジェクト」なので、第1引数が整数、第2引数がオブジェクトとなる関数を定義すればよい。
ちなみに
int operator + ( int a, int b );
のような組み込み型のみに対してのオーバーロードは禁止されているらしい。
(そりゃそうだ...)