単項演算子とは「−」「+」「++」「--」などを指します。
※他にもありますが、割愛します。
「+」や「−」は
a = b + c; a = b - c; |
とは違います。
↑のは二項演算子(左辺と右辺がある)です。
単項演算子は、
a = +b; a = -b; |
と言う使い方をする演算子です。
「+」はあまり使いませんので、「−」の説明をしましょう。
単項演算子は二項演算子と違って引数として受け取るものがありません。
そこで次のように使います。
<sample program cpp067-01>
#include <iostream> class Sample { public: Sample(); Sample(const int value); void Show() const; Sample operator-() const; private: int m_value; }; int main() { Sample sample1(5); Sample sample2; sample2 = -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 Sample::operator-() const { return Sample(-m_value); } |
<実行結果>
m_value = -5 続行するには何かキーを押してください・・・
プロトタイプ宣言を見ると、
Sample operator-() const; |
のように、引数はありません。
本体は、
Sample Sample::operator-() const { return Sample(-m_value); } |
コンストラクタ関数の引数に、マイナスの符号をつけたメンバ変数を渡しています。
もちろん、一時的なtempクラスを使って、
Sample Sample::operator-() const { Sample temp; temp.m_value = -m_value; return temp; } |
のように書いても大丈夫です。
インクリメントやデクリメント演算子は「前置」と「後置」があります。
それをどのように区別するかがポイントになります。
「前置」は上の「−」と同じイメージで作れます。
なお、自分自身は1増えますから「const」は付けられません。
<sample program cpp067-02>
#include <iostream> class Sample { public: Sample(); Sample(const int value); void Show() const; Sample& operator++(); private: int m_value; }; int main() { Sample sample1(0); Sample sample2; sample2 = ++sample1; sample1.Show(); 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++() { ++m_value; return *this; } |
<実行結果>
m_value = 1 m_value = 1 続行するには何かキーを押してください・・・
プロトタイプ宣言を見てみると、
Sample& operator++(); |
引数は無く、戻り値はクラスの参照になっています。
本体は、
Sample& Sample::operator++() { ++m_value; return *this; } |
メンバ変数m_valueをインクリメントして、「thisポインタ」の中身(クラスの実体)を返しています。
実行結果を見ると、先にsample1のメンバ変数が1増加して、その後にsample2に代入されている事が分かります。
※本体のインクリメントは前置である必要はありません。
続いて後置インクリメント演算子のオーバーロードを作ってみます。
前置とは違って、メンバ変数を増加させる前の値を保持しておかなければなりません。
また、同じ演算子のオーバーロードになりますので、引数が異ならなければ宣言も出来ません。
どのように違うのか、まずは書いてみます。
<sample program cpp067-03>
#include <iostream> class Sample { public: Sample(); Sample(const int value); void Show() const; Sample operator++(int); private: int m_value; }; int main() { Sample sample1(0); Sample sample2; sample2 = sample1++; sample1.Show(); 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++(int) { Sample temp = *this; ++m_value; return temp; } |
<実行結果>
m_value = 1; m_value = 0; 続行するには何かキーを押してください・・・
まずはプロトタイプ宣言を見てみましょう。
Sample operator++(int); |
引数は前置と区別するために「int」と書いてあります。
戻り値はクラスの参照ではなく「クラスのコピー」になっている事にも注目してください。
なぜこのようになっているのか、本体を見てみます。
Sample Sample::operator++(int) { Sample temp = *this; ++m_value; return temp; } |
メンバ変数が増加する前の状態を保持するため、一時的なクラスを作って自分自身をコピーしています。
その後、メンバ変数をインクリメントして、保持していた方のクラスを返しています。
こうする事で、自分自身のメンバ変数は1増え、増加前のクラスを戻す事が出来ています。
tempは一時的なローカルスコープのクラスですから、この関数が終わったら領域が解放されます。
解放される領域の参照を戻す事は非常に危険(ダングリングポインタ)ですから、コピーを返すようにしています。