講義メモ

テキスト編: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);
    }
}

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です