演算子のオーバーロード (演算子の多重定義)(教科書p.177〜204)
初級編では、関数のオーバーロードを学びました。関数のオーバーロードとは、異なる関数に同じ名前を与えることでした。例として、intの絶対値を返す関数、longの絶対値を返す関数がともに「abs」という名前で定義されているサンプルリストがあげられていましたね。(List2-5)
ここでは、演算子のオーバーロードについて説明します。
これまでは、int型 + int型 のように組込み型のものどうしの演算しか行っていませんでしたが、演算子をオーバーロードすると 自分で作ったクラスのオブジェクト + 自分で作ったクラスのオブジェクトのように、演算子に新しい意味を追加できます。
課題
課題3−1
- 教科書p.181-182のサンプルプログラム(例6.2-2)を入力し、コンパイル、実行してみなさい。
(プロジェクト名はp3kadai2w3とする。ソースファイル1つでよい。)
解説
(1)ポイント
- 関数のオーバーロードに似ているが、規則がいくつか加わる。
- 何らかの型(クラス)に関連付けて定義する。
- 演算子の優先順位は変更できない。
(演算子の優先順位は演習1課題1の3.6節を読み直して思い出そう。)
- 演算子が受け取るオペランドの数は変更できない。(例えば、2項演算子 + の場合、オペランドは2つ。)
- オーバーロードできない演算子は、 .(ドット演算子)、 :: (スコープ解決演算子)、 ?: (3項演算子)、 .* (メンバポインタを参照するドット演算子) の4つ、および、プリプロセッサ演算子。
- すでにある演算子に新しい意味を追加できるということであり、ユーザ独自の新しい演算子が作れるわけではない。(例えば、^_^ といった形の演算子を自由に作れるわけではない。)
(2)2項演算子のオーバーロード(p.179〜)
冗長だが、thisを使って以下のようにも書ける。
coord coord::operator+(coord ob2)
{
coord temp;
temp.x = this->x + ob2.x;
temp.y = this->y + ob2.y;
return temp;
}
|
※thisはキーワードであるため、変数名等には使えないので注意すること
このほかのポイント
- 左のオペランドは演算子の定義対象になっているクラスのオブジェクトにしかできない。(暗黙のうちに渡されるため。)
- *thisを返すように作ると、左のオペランドが演算によって変更される。(p.182代入演算子 = 例6.2-2後半)
- 右のオペランドはint型やdouble型など組み込み型のオブジェクトにすることもできる。(pp.183-184 例6.2-3)
- 参照仮引数を使用することもできる。(p.185例6.2-4)
課題3−2
- 先ほど入力したサンプルプログラム(例6.2-2)を変更し,演算子 * と 演算子 / をcoordに対してオーバーロードしなさい.(教科書p.186 練習問題6.2-1)
- (余力のある人は)ベクトル(coordオブジェクト)とスカラー値との乗算、および、スカラー値とベクトルとの乗算ができるように、演算子 * をオーバーロードしなさい.ベクトルとスカラー値との乗算は教科書を参考にするとよい.スカラー値とベクトルとの乗算には,フレンド関数を使う.
ここまでの補足説明スライド
解説のつづき
(3)関係演算子、論理演算子のオーバーロード(p.186〜)
- 関係演算子 == や !=、論理演算子 && (論理積) や || (論理和) をオーバーロードすることができる。
- 関数の返り値としてbool型の値を返すように作ること。
- int型ではなくbool型を用いることでより真偽を表すことが明確になる。
- bool型の場合は、真ならばtrue、偽ならばfalseを返すようにつくる。
- int型の場合は、真ならば非0、偽ならば0を返すようにつくる。
// 関係演算子 == のオーバーロード
bool coord::operator==(coord ob2)
{
return x == ob2.x && y == ob2.y;
}
// 論理演算子 && のオーバーロード
bool coord::operator&&(coord ob2)
{
return (x && ob2.x) && (y && ob2.y);
}
|
(4)単項演算子のオーバーロード(p.188〜)
- 単項演算子(インクリメント演算子 ++ やデクリメント演算子 --、- (負符号) など)をオーバーロードすることができる。
- オペランドが1つしかないので仮引数は必要ない。
- ただし、新しいC++では、前置形式と後置形式のインクリメント/デクリメントを区別するために、int型の仮引数を使用する。(p.189例6.4-2)
// 単項演算子 - (負符号)のオーバーロード
// 仮引数は不要
coord coord::operator-()
{
x = -x;
y = -y;
return *this;
}
// ++演算子(前置形式)のオーバーロード
coord coord::operator++()
{
x++;
y++;
return *this;
}
// ++演算子(後置形式)のオーバーロード
// (2008.6.6 変更前の値を返すように修正)
coord coord::operator++(int notused)
{
coord tmp = *this;
x++;
y++;
return tmp;
}
|
(5)添え字演算子のオーバーロード(p.200)
- (配列の)添字演算子 [ ]をオーバーロードすることができる。
- int型の仮引数を使って添字を指定する
- メンバ関数でしかオーバーロードできない。
const int SIZE = 10;
class arraytype {
double a[SIZE];
public:
...
};
// [ ] 演算子のオーバーロード
// 「代入」もできるように「参照」を返す
double &arraytype::operator[](int i)
{
if (i < 0 || i >= SIZE) {
cerr << "エラー!範囲外です。";
cerr << "添字 i = " << i << endl;
exit(1);
}
return a[i];
}
|
(6)フレンド演算子関数の使用(p.191)
- フレンド関数は、メンバ関数ではないので、thisポインタを持たない。このため、
- 左のオペランドが暗黙のうちに渡されることはない。
- 代入演算子のオーバーロードにはフレンドを使用できない。
- オペランドを明示的に引数として渡す必要がある。
- 左のオペランドを intや doubleなどの組み込み型にできる。
(メンバ演算子関数では実現できない)
課題3−3
- coordクラスに上記二つのフレンド演算子を追加して実装し、コンパイル、実行してみなさい。