テキスト編:p.345 演算子のオーバーロードとは から
ゲーム開発演習:敵機の出現・移動のフォロー、自弾と敵機の衝突判定
p.345 演算子のオーバーロードとは
・2項+演算子のように、オペランドの型によって作用が異なるものがあり、これを演算子のオーバーロードという ・組み込み型(p.38)に対する演算子のオーバーロードはシステムが定義しており、変更できない ・しかし、ユーザ定義型に対する演算子のオーバーロードは、ユーザ定義型を表すクラスの中で定義することが可能 ・例えば、X座標とY座標を持つ座標クラスのオブジェクトどうしを2項+演算子で演算できるように定義しておけば、 座標 a(3, 2)、b(1, 5) において、a + b を(3 + 2, 2 + 5) = (5, 7) とすることが可能 ・テキストでは複素数を表すクラスを例にとって、複素数の演算公式を演算子のオーバーロードで実装している ・しかし、数学的知識がある前提になっているので、X座標とY座標を持つ座標クラスに置き換えて理解しよう
p.347(X座標とY座標を持つ座標クラス)map01.cs
・テキストp.347のcomplex01.csの仕様を下記のように変更して、演算子のオーバーロードを実装する基にしよう ・クラス名:Map ・インスタンス変数:int x, y ・プロパティ:int X(x用), int Y(y用) ・引数のないコンストラクタ: MAP() ⇒ x←0、y←0 ・int型2引数を持つコンストラクタ: MAP(x, y) ・上記の動作を確認するMain()メソッド
作成例
//p.347 map01.cs(complex01.cs改)
using System;
class Map { //座標クラス
int x; //X座標のデータメンバ
int y; //Y座標のデータメンバ
public int X { //X座標用のプロパティ
get { return x; }
set { x = value; }
}
public int Y { //Y座標用のプロパティ
get { return y; }
set { y = value; }
}
public Map() { //デフォルトコンストラクタ
x = y = 0;
}
public Map(int x, int y) { //X座標、Y座標を指定するコンストラクタ
this.x = x;
this.y = y;
}
}
class Map01 {
public static void Main() {
Map A = new Map(); //座標A(0,0)を生成
A.X = 10;
A.Y = 5; //座標(10, 5)になる
Map B = new Map(2, 3); //座標B(2,3)を生成
Console.WriteLine("座標Aは({0}, {1})です。", A.X, A.Y);
Console.WriteLine("座標Bは({0}, {1})です。", B.X, B.Y);
}
}
p.349 単項演算子のオーバーロード
・クラス内でoperatorキーワードを用いてメソッドに近い表記をすることで、演算子のオーバーロードが可能
・単項演算子のオーバーロードでは、public staticで「戻り値型 operator 演算子(引数型 仮引数)」を定義し、
内部で演算結果をreturnすれば良い
・演算子のオーバーロードの基本ルールとして、元の演算子の意味とかけ離れた定義を行わないことがある
・よって、単項-演算子のオーバーロードでは、戻り値型と引数型をクラスに合わせると良い。よって:
public static クラス名 operator-(クラス名 仮引数) { return 仮引数の符号反転結果; }
・これを座標クラスMapに当てはめると下記のようになり、X座標とY座標の符号反転になる
public static Map operator-(Map w) { return new Map(-w.x, -w.y); }
※ テキストのopov01.csでは継承したクラスにおいて単項演算子のオーバーロードを記述しているが、継承する必要はない
p.349 Map02.cs:単項-演算子のオーバーロード
・Map01.csに単項-演算子のオーバーロードを追記して動作を確認しよう。 ・単項-演算子により、X座標とY座標の符号反転を行うとする。 ・よって、MapクラスのオブジェクトA(x, y)について、-Aは(-x, -y)となる。
作成例
//p.347 map02.cs(opov01.cs改)
using System;
class Map { //座標クラス
int x; //X座標のデータメンバ
int y; //Y座標のデータメンバ
public int X { //X座標用のプロパティ
get { return x; }
set { x = value; }
}
public int Y { //Y座標用のプロパティ
get { return y; }
set { y = value; }
}
public Map() { //デフォルトコンストラクタ
x = y = 0;
}
public Map(int x, int y) { //X座標、Y座標を指定するコンストラクタ
this.x = x;
this.y = y;
}
public static Map operator-(Map w) { //単項-演算子のオーバーロード
return new Map(-w.x, -w.y); //X座標とY座標の符号を反転した結果を返す
}
}
class Map01 {
public static void Main() {
Map A = new Map(2, 3); //座標B(2,3)を生成
Map B = -A; //単項-演算子のオーバーロードを用いる
Console.WriteLine("座標Aは({0}, {1})です。", A.X, A.Y);
Console.WriteLine("座標Bは({0}, {1})です。", B.X, B.Y);
}
}
p.349 Map03.cs:単項+演算子のオーバーロード
・Map02.csに単項+演算子のオーバーロードを追記して動作を確認しよう。 ・単項+演算子により、座標(0,0)から座標までの距離を実数で返すとする。 ・例えば、MapクラスのオブジェクトA(-3, -4)について、+Aは5.0となる。 ・距離は3平方の定理で(X座標の2乗+Y座標の2乗)の平方根で得られる ・平方根はdouble Math.Sqrt(double)メソッドで得ると良い
作成例
//p.347 map03.cs(単項*演算子のオーバーロード)
using System;
class Map { //座標クラス
int x; //X座標のデータメンバ
int y; //Y座標のデータメンバ
public int X { //X座標用のプロパティ
get { return x; }
set { x = value; }
}
public int Y { //Y座標用のプロパティ
get { return y; }
set { y = value; }
}
public Map() { //デフォルトコンストラクタ
x = y = 0;
}
public Map(int x, int y) { //X座標、Y座標を指定するコンストラクタ
this.x = x;
this.y = y;
}
public static Map operator -(Map w) { //単項-演算子のオーバーロード
return new Map(-w.x, -w.y); //X座標とY座標の符号を反転した結果を返す
}
public static double operator +(Map w) { //単項+演算子のオーバーロード
return Math.Sqrt(w.x * w.x + w.y * w.y); //各座標の2乗の和の平方根を返す
}
}
class Map01 {
public static void Main() {
Map A = new Map(-3, -4); //座標Aを生成
Console.WriteLine("座標Aは({0}, {1})で、距離は{2}", A.X, A.Y, +A);
}
}
p.353 2項演算子のオーバーロード
・2項演算子のオーバーロードでは、public staticで「戻り値型 operator 演算子(引数型① 仮引数①, 引数型② 仮引数②)」 を定義し、内部で演算結果をreturnすれば良い ・左オペランドが仮引数①、右オペランドが仮引数②となる
p.349 Map04.cs:2項+演算子のオーバーロード (opov02.cs改)
・Map02.csに2項+演算子のオーバーロードを追記して動作を確認しよう。 ・2項+演算子により、A + B は各座標の和をもつMapオブジェクトを返すとする。 ・例えば、MapクラスのオブジェクトA(-3, -4)、B(4, 2)について、A + Bは(1, -2)となる。
作成例
//p.349 Map04.cs:2項+演算子のオーバーロード (opov02.cs改)
using System;
class Map { //座標クラス
int x; //X座標のデータメンバ
int y; //Y座標のデータメンバ
public int X { //X座標用のプロパティ
get { return x; }
set { x = value; }
}
public int Y { //Y座標用のプロパティ
get { return y; }
set { y = value; }
}
public Map() { //デフォルトコンストラクタ
x = y = 0;
}
public Map(int x, int y) { //X座標、Y座標を指定するコンストラクタ
this.x = x;
this.y = y;
}
public static Map operator -(Map w) { //単項-演算子のオーバーロード
return new Map(-w.x, -w.y); //X座標とY座標の符号を反転した結果を返す
}
public static double operator +(Map w) { //単項+演算子のオーバーロード
return Math.Sqrt(w.x * w.x + w.y * w.y); //各座標の2乗の和の平方根を返す
}
public static Map operator +(Map a, Map b) { //2項+演算子のオーバーロード
return new Map(a.x + b.x, a.y + b.y); //各座標の和をもつオブジェクトを返す
}
}
class Map01 {
public static void Main() {
Map A = new Map(-3, -4); //座標Aを生成
Map B = new Map(4, 2); //座標Bを生成
Console.WriteLine("A + Bは({0}, {1})", (A + B).X, (A + B).Y);
}
}
p.356(2項==演算子のオーバーロード )
・2項==演算子は特別扱いの演算子で、オーバーロードする場合、bool型を返す必要がある ・また、同時に2項!=演算子もオーバーロードする必要がある ・なお、内容は「return !(仮引数① == 仮引数②);」固定で良い ※ テキストにはEqualsメソッドとGetHashCodeメソッドのオーバーライドも必要とあるが、必須ではない
p.358 Map05.cs:2項==演算子、2項!=演算子のオーバーロード (opov03.cs改)
・Map04.csに2項==演算子、2項!=演算子のオーバーロードを追記して動作を確認しよう。 ・2項==演算子により、A == B は各座標が共に等しければtrueを、でなければfalseを返すとする。 ・2項!=演算子により、A != B は各座標が共に等しければfalseを、でなければtrueを返すとする。
作成例
//p.349 Map04.cs:2項+演算子のオーバーロード (opov02.cs改)
using System;
class Map { //座標クラス
int x; //X座標のデータメンバ
int y; //Y座標のデータメンバ
public int X { //X座標用のプロパティ
get { return x; }
set { x = value; }
}
public int Y { //Y座標用のプロパティ
get { return y; }
set { y = value; }
}
public Map() { //デフォルトコンストラクタ
x = y = 0;
}
public Map(int x, int y) { //X座標、Y座標を指定するコンストラクタ
this.x = x;
this.y = y;
}
public static Map operator -(Map w) { //単項-演算子のオーバーロード
return new Map(-w.x, -w.y); //X座標とY座標の符号を反転した結果を返す
}
public static double operator +(Map w) { //単項+演算子のオーバーロード
return Math.Sqrt(w.x * w.x + w.y * w.y); //各座標の2乗の和の平方根を返す
}
public static Map operator +(Map a, Map b) { //2項+演算子のオーバーロード
return new Map(a.x + b.x, a.y + b.y); //各座標の和をもつオブジェクトを返す
}
public static bool operator ==(Map a, Map b) { //2項==演算子のオーバーロード
return a.x == b.x && a.y == b.y; //各座標の和をもつオブジェクトを返す
}
public static bool operator !=(Map a, Map b) { //2項!=演算子のオーバーロード
return !(a == b); //2項==演算子のオーバーロードの結果を反転して返す
}
}
class Map01 {
public static void Main() {
Map A = new Map(-3, -4); //座標Aを生成
Map B = new Map(4, 2); //座標Bを生成
Console.WriteLine("A == Bは{0}、A != Bは{1}", A == B, A != B);
}
}