2017年10月29日
《その96》 演算子関数( operator++, operator-- の概要 その3 )(p.433演習12-1)
増分演算子には前置・後置の2種類がありました。また、減分演算子にも前置・後置の2種類がありました。
C& operator++(); // 前置増分演算子
C operator++(int); // 後置増分演算子
C& operator--(); // 前置減分演算子
C operator--(int); // 後置減分演算子
次のコードは、増分演算子の前置タイプと後置タイプの例です。
Coordinates& operator++() { // 前置タイプ
++position;
return *this;
/*
↑ (1) 自分自身である *this のデータメンバ position をインクリメント。
(2) 自分自身への参照を返却。
*/
}
Coordinates operator++(int) { // 後置タイプ
Coordinates x = *this;
++position;
return x;
/*
↑ (1) 自分自身である *this のコピー x を作る。
(2) 自分自身である *this のデータメンバ position をインクリメント。
(3) コピー x(インクリメント前の自分自身)を返却する。
*/
}
後置タイプの場合は、自分自身である *this のコピーをいったん保存しそのコピーを返す必要があり、その分だけコストが多くかかることがわかります。
新版明解C++入門編 p.433 演習12-1
下記のカウンタクラス Counter では、前置演算子と後置演算子は行うことがほぼ同じであるため、似たようなコードが各演算子関数に散らばっている(たとえば、増分演算子中の if文は前置版と後置版で同一であるし、減分演算子中の if文も前置版と後置版で同一である)。
前置演算子関数から後置演算子関数を呼び出すか、もしくは後置演算子関数から前置演算子関数を呼び出すかのいずれかに変更せよ。なお、どちらの実現が好ましいのかも検討すること。
課題のプログラムは------------------------------------------
Counter.h
CounterTest.cpp
の2つで構成されています。
// カウンタクラス Counter------------------------------------------
#ifndef ___Class_Counter
#define ___Class_Counter
#include <climits>
//===== カウンタクラス =====//
class Counter {
unsigned cnt; // カウンタ
public:
//--- コンストラクタ ---//
Counter() : cnt(0) { }
//--- unsigned型への変換関数 ---//
operator unsigned() const { return cnt; }
//--- 論理否定演算子! ---//
bool operator!() const { return cnt == 0; }
//--- 前置増分演算子++ ---//
Counter& operator++() {
if (cnt < UINT_MAX) cnt++; // カウンタの上限はUINT_MAX
return *this; // 自分自身への参照を返却
}
//--- 後置増分演算子++ ---//
Counter operator++(int) {
Counter x = *this; // インクリメント前の値を保存
if (cnt < UINT_MAX) cnt++; // カウンタの上限はUINT_MAX
return x; // 保存していた値を返却
}
//--- 前置減分演算子-- ---//
Counter& operator--() {
if (cnt > 0) cnt--; // カウンタの下限は0
return *this; // 自分自身への参照を返却
}
//--- 後置減分演算子-- ---//
Counter operator--(int) {
Counter x = *this; // デクリメント前の値を保存
if (cnt > 0) cnt--; // カウンタの下限は0
return x; // 保存していた値を返却
}
};
#endif
// カウンタクラス Counter の利用例 Counter.cpp
#include <iostream>
#include "Counter.h"
using namespace std;
int main()
{
int no;
Counter x;
Counter y;
cout << "カウントアップ回数:"; cin >> no;
for (int i = 0; i < no; i++) // カウントアップ(xは後置でyは前置)
cout << x++ << '\t' << ++y << '\n';
cout << "カウントダウン回数:"; cin >> no;
for (int i = 0; i < no; i++) // カウントダウン(xは後置でyは前置)
cout << x-- << '\t' << --y << '\n';
if (!x)
cout << "xは0です。\n";
else
cout << "xは0ではありません。\n";
}
以下が、課題の変更を行った後のプログラムです。
// Counter.h
#ifndef ___Class_Counter
#define ___Class_Counter
#include <climits>
class Counter {
unsigned cnt;
public:
Counter() : cnt(0) { }
operator unsigned() const { return cnt; } // 変換関数
Counter& operator++() { // 前置増分演算子関数
if (cnt < UINT_MAX) cnt++;
return *this;
}
Counter operator++(int) { // 後置増分演算子関数
Counter tmp = *this;
operator++(); /* 前置増分演算子関数を呼び出す */
return tmp;
}
Counter& operator--() { // 前置減分演算子関数
if (cnt > 0) cnt--;
return *this;
}
Counter operator--(int) { // 後置減分演算子関数
Counter tmp = *this;
operator--(); /* 前置減分演算子関数を呼び出す */
return tmp;
}
};
#endif
// CounterTest.cpp
#include <iostream>
#include "Counter.h"
using namespace std;
int main()
{
int no;
Counter x, y;
cout << "カウントアップ回数:"; cin >> no;
for (int i = 0; i < no; i++)
cout << x++ << '\t' << ++y << '\n';
cout << "カウントダウン回数:"; cin >> no;
for (int i = 0; i < no; i++)
cout << x-- << '\t' << --y << '\n';
if (!x)
cout << "xは0です。\n";
else
cout << "xは0ではありません。\n";
}
後置演算子関数から前置演算子関数を呼び出すほうが好ましいです。
その場合、後置演算子関数は、前置演算子関数を呼び出すことでカウンターをインクリメントすることになります。
前置演算子関数から後置演算子関数を呼び出す場合は、前置演算子関数は、後置演算子関数を呼び出すことでカウンターをインクリメントすることになります。そのようにすると、後置演算子関数内での自分自身である *this のコピーをいったん保存しそのコピーを返すというステップを、前置演算子関数も踏まなければならないことになりコスト的に不利になります。
--
この記事へのコメント
コメントを書く
この記事へのトラックバックURL
https://fanblogs.jp/tb/6909508
※ブログオーナーが承認したトラックバックのみ表示されます。
この記事へのトラックバック