演算子多重定義
演算子多重定義とは、演算子に複数の機能を持たせる事です。
この機能によって非常に多彩な処理が可能になります。
なぜこの機能が必要なのかと言えば、
ユーザ定義型(クラス)が存在するからです。
例えば以下のようなプログラムがあったとします。
class myclass {
public:
int a;
};
void main(void) {
myclass obj1, obj2, obj3;
obj3.a = obj1.a + obj2.a;
}
このプログラムは、myclassのメンバ変数aを足す例です。
何も不思議な事はないごく普通のプログラムですが、
以下のような書き方ができれば便利だと思いませんか?
class myclass {
public:
int a;
};
void main(void) {
myclass obj1, obj2, obj3;
obj3 = obj1 + obj2;
}
この足し算は通常、コンパイルエラーとなり、実行できませんが、
クラス同士の足し算を独自に実装する機能、
すなわち、演算子多重定義を使えば、
このようなクラス同士の足し算が実現可能になります。
この場合、+(プラス)演算子を多重定義する形になります。
この演算子の多重定義を実装する際にはポイントがあります。
「演算子には、第1の値と第2の値を演算して第3の値とする。」
と言うルールがあります。
この3つの値(データ型)によって演算子の多重定義の中身が変わります。
演算子の多重定義を実装する方法は3つあります。
1つ目は、クラスのメンバ関数で実装する方法。
2つ目は、クラスに対応するフレンド関数で実装する方法。
3つ目は、通常の関数で実装する方法です。
クラスのメンバ関数で実装する方法。
class myclass {
public:
int a;
myclass operator+(myclass obj2) {
myclass temp;
temp.a = this->a + obj2.a;
return temp;
}
};
void main(void) {
myclass obj1, obj2, obj3;
obj3 = obj1 + obj2;
}
このプログラムは、
myclassのメンバ関数で演算子多重定義を実装した例です。
演算子多重定義のための関数は特殊な書き方が必要です。
operator+と表記されていますが、
これは、演算子+(プラス記号)の事です。
演算子-(マイナス記号)の場合は、operator-となります。
そして、実際に使われているコードは、
obj3 = obj1 + obj2;
となっています。
これは、obj3 = obj1.operator+(obj2);
と言うように関数形式として認識されます。
ここが非常に理解しづらい部分ですが、最重要ポイントです。
つまり、obj1のoperator+関数が呼ばれ、引数にはobj2が渡される。
と言う意味になります。
この意味を理解できれば、メンバ関数内の記述も自然と理解できる事でしょう。
クラスに対応するフレンド関数で実装する方法
上記のプログラムをフレンド関数で実装する例をご紹介します。
class myclass {
private:
int a;
friend myclass operator+(myclass obj1, myclass obj2);
};
myclass operator+(myclass obj1, myclass obj2) {
myclass temp;
temp.a = obj1.a + obj2.a;
return temp;
}
void main(void) {
myclass obj1, obj2, obj3;
obj3 = obj1 + obj2;
}
フレンド関数として実装すると、引数が2つに増えています。
つまり、フレンド関数には
演算子の第1の値(クラス)のthisポインタが渡されないので、
引数として、第1の値(クラス)、
第2の値(クラス)を記述する事が必要になります。
こう言った記述方法は仕様ですので、
多少分かりづらくても仕方がありません。
このプログラム内では、メンバ変数がprivate指定されていますが、
フレンド関数で実装されているので、
メンバ変数aにアクセス可能となっています。
通常の関数で実装する方法
上記のプログラムを通常の関数で実装する例をご紹介します。
class myclass;
myclass operator+(myclass obj1, myclass obj2);
class myclass {
public:
int a;
};
myclass operator+(myclass obj1, myclass obj2) {
myclass temp;
temp.a = obj1.a + obj2.a;
return temp;
}
void main(void) {
myclass obj1, obj2, obj3;
obj3 = obj1 + obj2;
}
まず、フレンド関数の場合と同じように引数は2つ記述が必要です。
通常の関数で実装すると、
関数内ではクラスのpublicメンバ変数にしかアクセスできない点が特徴です。
上の2行はプロトタイプ宣言です。
異なるデータ型同士の演算子多重定義
ここまでは同じクラス(データ型)同士の演算子多重定義を説明しましたが、
異なるデータ型同士の場合でも演算子の多重定義を実装する事が可能です。
例えば以下のような場合です。
void main(void) {
myclass obj1, obj2, obj3;
obj3 = obj1 + 5;
obj3 = 5 + obj2;
}
まず、1つ目の obj1 + 5; の記述に対応する演算子多重定義関数を実装してみます。
class myclass {
public:
int a;
myclass(int data) { a = data; }
};
myclass operator+(myclass obj2) {
myclass temp;
temp.a = this->a + obj2.a;
return temp;
}
void main(void) {
myclass obj1, obj2, obj3;
obj3 = obj1 + obj2;
}
obj1 + 5; の記述は、myclass型 + int型 となるため、
myclass operator+(int data); のような関数を追加しても良いですが、
1つの演算子多重定義関数で対応できるようになっています。
5と言う数値がoperator+関数の引数(myclass型)に
渡せるようになっています。
これは、5がmyclass型に変換(つまり、myclass(5)と変換)されます。
myclass(5)と言うのは、コンストラクタを呼ぶ動作となります。
つまり、対応するメンバ変数に値を設定するコンストラクタが存在すれば、
新たにmyclass operator+(int data); と言う関数を作る必要はなくなります。
次に2つ目の 5 + obj2; の記述ですが、これは少し厄介な感じです。
今までの考え方によると、
5.operator+(・・・); のような感じになってしまいます。
5と言うのは、クラスではないので、こういう書き方はできません。
こういった場合は、
クラスのメンバ関数による演算子多重定義関数は作れません。
フレンド関数か通常の関数で対応する必要があります。
フレンド関数と通常の関数では、
対応するクラスに対するアクセス権だけが異なるので、
ここでは、フレンド関数のみご紹介します。
class myclass {
private:
int a;
friend myclass operator+(myclass obj1, myclass obj2);
friend myclass operator+(int data1, myclass obj2);
};
myclass operator+(myclass obj1, myclass obj2) {
myclass temp;
temp.a = obj1.a + obj2.a;
return temp;
}
myclass operator+(int data1, myclass obj2) {
myclass temp;
temp.a = data1 + obj2.a;
return temp;
}
void main(void) {
myclass obj1, obj2, obj3;
obj3 = obj1 + obj2;
obj3 = obj1 + 5;
obj3 = 5 + obj2;
}