算術演算子は「+」「−」「*」「/」「%」があります。
どれも使い方は同じなので、作り方のバリエーションを書きます。
すでに「+」演算子を使って、2つのバリエーションは書きました。
変 数 = クラス + クラス クラス = クラス + クラス |
演算のイメージからすると、他にも
変 数 = クラス + 変 数 クラス = クラス + 変 数 変 数 = 変 数 + クラス クラス = 変 数 + クラス |
などが考えられそうです。
ちなみに、
クラス = 変 数 + 変 数 |
は「+」演算子ではなく「=」演算子のオーバーロードになります。
イメージ的には、
int result = sample + 10; |
のような計算が出来る演算子オーバーロードを作ってみます。
<sample program cpp064-01>
#include <iostream> class Sample { public: Sample(const int value); int operator+(const int rhs) const; private: int m_value; }; int main() { Sample sample(12); int result = sample + 15; std::cout << "result = " << result << std::endl; return 0; } Sample::Sample(const int value) : m_value(value) { } int Sample::operator+(const int rhs) const { return m_value + rhs; } |
<実行結果>
27 続行するには何かキーを押してください・・・
こちらは、引数を変数にするだけですから、そんなに変わったところはありません。
これも戻り値がクラスになっていますから前に作ったプログラムを少し変更する程度なのですが、折角なので新しい事も説明出来るよう作ってみます。
<sample program cpp064-02>
#include <iostream> class Sample { public: Sample(); Sample(const int value); void Show() const; Sample operator+(const int rhs) const; private: int m_value; }; int main() { Sample sample1(12); Sample sample2; sample2 = sample1 + 15; sample2.Show(); return 0; } Sample::Sample() { } Sample::Sample(const int value) : m_value(value) { } void Sample::Show() const { std::cout << "m_value = " << m_value << std::endl; } Sample Sample::operator+(const int rhs) const { Sample temp; temp.m_value = m_value + rhs; return m_value + rhs; } |
<実行結果>
m_value = 27 続行するには何かキーを押してください・・・
まず、
Sample operator+(const int rhs) const; |
演算子オーバーロードの戻り値をクラスにしました。
本体は、
Sample Sample::operator+(const int rhs) const { Sample temp; temp.m_value = m_value + rhs; return m_value + rhs; } |
右辺が変数になっている事以外は前に作ったプログラムと同じく、tempと言う一時的に作ったクラスに結果を入れ返しています。
また、tempを作るため「既定のコンストラクタ」を追加しました。
Sample::Sample() { } |
これまでは、初期値として初期化リストで0をセットしていましたが、特に必要なければ書く必要はありません。
さらに、メンバ変数を確認するためShow関数を加えました。
さて、新しい事についてですが、演算子オーバーロードの関数本体の書き方に関する事です。
Sample Sample::operator+(const int rhs) const { Sample temp; temp.m_value = m_value + rhs; return m_value + rhs; } |
この中身ですが、引数を持ったコンストラクタ関数があるので、
Sample Sample::operator+(const int rhs) const { return Sample(m_value + rhs); } |
このように書く事が出来ます。
いきなりコンストラクタ関数を呼び出し引数を渡して実体を作り、それを返しています。
実体に名前を付ける必要もありませんし、「既定のコンストラクタ」を作らなくても動きます。
※このサンプルでは、main関数で「sample2」を作る際に「既定のコンストラクタ」が必要です。
さて、この形式は、
int result = 10 + sample; |
のような計算をイメージしたものです。
これを実現しようとすると、これまでの知識では出来ません。
演算子オーバーロードは「左辺のクラスから呼び出される関数」のようなものです。
右辺を引数として受け取りますから、逆になると作れないのです。
これを解決するには「フレンド関数」を知らなければなりません。
「フレンド関数」はメンバ関数ではなく、クラスの外に本体を書きます。
メンバ関数では無い関数からは、当然privateなメンバ変数にアクセス出来ません。
しかし、プロトタイプ宣言に「friend」のキーワードを付けて、クラス内に置いておく事で、その関数とクラスは「フレンド」になり、メンバ変数にアクセス出来るようになります。
<sample program cpp064-03>
#include <iostream> class Sample { public: Sample(const int value); friend int operator+(const int lhs, const Sample &rhs); private: int m_value; }; int main() { Sample sample(12); int result = 13 + sample; std::cout << "result = " << result << std::endl; return 0; } Sample::Sample(const int value) : m_value(value) { } int operator+(const int lhs, const Sample &rhs) { return lhs + rhs.m_value; } |
<実行結果>
result = 25 続行するには何かキーを押してください・・・
まずは演算子オーバーロードの関数本体を見てみます。
int operator+(const int lhs, const Sample &rhs) { return lhs + rhs.m_value; } |
メンバ関数では無いので所属はありません。
引数は第1引数(左辺)が変数、第2引数(右辺)がクラスです。
右辺のクラスは関数内部でメンバ変数「m_value」にアクセスしています。
本来ならエラーですが、Sampleクラス内に、
friend int operator+(const int lhs, const Sample &rhs); |
「フレンド」宣言されたプロトタイプ宣言がありますので、エラーになりません。
これも「フレンド関数」を使って作ります。
<sample program cpp064-04>
#include <iostream> class Sample { public: Sample(); Sample(const int value); void Show() const; friend Sample operator+(const int lhs, const Sample &rhs); private: int m_value; }; int main() { Sample sample1(12); Sample sample2; sample2 = 13 + sample1; sample2.Show(); return 0; } Sample::Sample() { } Sample::Sample(const int value) : m_value(value) { } void Sample::Show() const { std::cout << "m_value = " << m_value << std::endl; } Sample operator+(const int lhs, const Sample &rhs) { return Sample(lhs + rhs.m_value); } |
<実行結果>
m_value = 25 続行するには何かキーを押してください・・・
変数 + クラス
という形式は必要無ければ作らなくても構わないと思います。
「フレンド関数」はクラス外の関数から隠蔽したメンバ変数にアクセス出来るため危険な関数と言えます。
使わなければならないケース以外では、あまり使いたく無いですね・・・