C++ 演算子多重定義とは?

演算子多重定義

演算子多重定義とは、演算子に複数の機能を持たせる事です。

この機能によって非常に多彩な処理が可能になります。

なぜこの機能が必要なのかと言えば、
ユーザ定義型(クラス)が存在するからです。

例えば以下のようなプログラムがあったとします。

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;
}