msty開発メモ

技術ネタを綴ります

【C++】演算子のオーバーロードの使い方(基礎)

演算子オーバーロードについてかる~くゆる~く触れようと思います。



演算子オーバーロードはクラスに対する拡張です。

といっても私ならそれだけ言われてもわからないので実際に演算子オーバーロードを使用するクラスを一緒に作っていきましょう。
基礎部分な学習なので、下手に混乱させないために最適化はしません。



最低限、コンストラクタとメソッドの区別がついていたらオーケーです。



以下のソースをコピペするなりしてください

#include<cstdio>

class Point {
public:
	int x;
	int y;


	Point(int _x, int _y) {
		this->x = _x;
		this->y = _y;
	}

};

int main() {
	
	Point p1(10, 20);


	printf("%d:%d\n", p1.x, p1.y);

}

これが一体何をしているのかわからないならクラスを少し勉強してきてから続きを読んでくださいね。

ではまず、このクラスに、別のクラスと比較して、X,Y両方の値が等しいかどうか調べるメソッドを書いてみます。演算子オーバーロードをしない実装方法ですね

おそらくこんな感じになるでしょう

#include<cstdio>

class Point {
public:
	int x;
	int y;


	Point(int _x, int _y) {
		this->x = _x;
		this->y = _y;
	}

	bool Equals(Point _point) {
		if (this->x == _point.x &&
			this->y == _point.y) {
			return true;
		}
		return false;
	}

};

int main() {
	
	Point p1(10, 20);
	Point p2(10, 20);

	printf("p1 = %d:%d\n", p1.x, p1.y);
	printf("p2 = %d:%d\n", p2.x, p2.y);

	if (p1.Equals(p2)) {
		printf("true\n");
	}
	else {
		printf("false\n");
	}


}

実際の呼び出しでは"p1.Equals(p2)"のようにしてますが、

p1 == p2

のようにしたほうが見やすくないですか?
ですが、こう書いてもコンパイルは通りません。
Equalsメソッドを書いてないのにEqualsメソッドを呼びだそうとしているようなものです。

なのでこのように書けるように演算子オーバーロードして処理を書いて上げる必要があります。

それでは、無心でも良いので以下のソースをPointクラスのEqualsメソッドの下にでも書いてください

bool operator==(Point _point) {
	if (this->x == _point.x &&
		this->y == _point.y) {
		return true;
	}
	return false;
}

書いたら、

p1.Equals(p2)

を、

p1 == p2

にしてみてください。
動作するはずです。

動作が確認できたところで、解説します。

まず、operator の前にある bool が戻り値です。
演算結果が帰ってきます。

p1 == p2 だと
今回の例だと trueが帰ってくるので、
それが1f文で条件判定され、真と判定されます。

次の operator が演算子オーバーロードするというキーワードです。これを書かないとオーバーロードできません。

その直後に書いてある、== がオーバーロードする演算子です。違和感しかないと思ったらメソッド名とでも思っておきましょう。

次の ()内の変数が引数です。注意なのが、演算子によって指定可能な引数の数が決まっていることです。

== は一つしか指定できないません。

次に{}内はその演算子での処理を記述します。今回だとEqualsメソッドと同じ動きですね。

さて、処理の記述方法、引数は一つまで、とわかったところですが、一つ問題にぶち当たります。
main関数の中身を以下のように書き換えてください。
具体的にはPoint変数が一つ増えてます。

int main() {
	
	Point p1(10, 20);
	Point p2(10, 20);
	Point p3(10, 20);

	printf("p1 = %d:%d\n", p1.x, p1.y);
	printf("p2 = %d:%d\n", p2.x, p2.y);

	if (p1==p2 && p1==p3) {
		printf("true\n");
	}
	else {
		printf("false\n");
	}


}

3つ比較するならこう書けます
Equlasメソッドを使った場合はこう書きます。

p1.Equals(p2) == p1.Equals(p3)

私は嫌です。書くのが面倒くさいです。

ではもう一つ、Pointクラス同士を加算する処理を書いてみましょう。
以下の処理をPointクラスに追加してください。

Point operator+(Point _point) {
	//このクラスのコピーを作成
	//コピーを作らないと元の値が変わってしまう。
	Point result(*this);

	result.x += _point.x;
	result.y += _point.y;
	return result;
}

それぞれの項目については先ほどの == メソッドと同じです。
実際にこれを使った処理をmain関数に書いてみましょう。

int main() {
	
	Point p1(10, 20);
	Point p2(10, 20);
	Point p3 = p1 + p2;

	printf("p1 = %d:%d\n", p1.x, p1.y);
	printf("p2 = %d:%d\n", p2.x, p2.y);
	printf("p3 = %d:%d\n", p3.x, p3.y);
}

Pointクラスでの+演算子を定義したことで、Pointクラス同士の計算ができました。
以下の様な複数の計算も可能です。
Point p4 = p1 + p2 + p3;

具体的には、まず、
p1+p2 が処理されます。
p1+p2の結果、Point型の値が返ってくるので、それとp3を計算します。
どちらもPoint型なので正しく計算できます。
そしてすべての計算が終われば、変数p4に計算されたPoint型の値が代入されます。

これらのように演算子オーバーロードして処理を書いてしまえば、いちいちメソッドを書くよりもより直感的に、なおかつシンプルに記述することができ、可読性が上がります。
別のクラス同士を計算したり比較することも自由にできるので、演算子を使って実装できそうな処理は演算子オーバーロードして実装するといいでしょう。