読者です 読者をやめる 読者になる 読者になる

Natural Software

KinectなどのDepthセンサーを中心に活動しています

インクリメント・デクリメント

プログラム C++

確か More Effective C++ に書いてあった、前置インクリメント(デクリメント)と後置インクリメント(デクリメント)の違い。
意外と気にしないものなんだろうか。
細かい話なので、気にしなくてもいいのかもしれないけど。


C++ のいいところは、C言語では見ることができない演算子の動作を見ることができることだと思う。
このインクリメントの他にも、初期化と代入の違い、代入演算子の実装について etc...


表題のインクリメントをオーバーロードした一番単純なコードはこうなるかな。

// インクリメント演算子サンプル
#include <iostream>

class Integer
{
public:
	Integer() : value_( 0 )
	{ std::cout << this << " : コンストラクタ" << std::endl; }

	Integer( Integer& rhs ) : value_( rhs.value_ )
	{ std::cout << this << " : コピーコンストラクタ" << std::endl; }

	~Integer()
	{ std::cout << this << " : デストラクタ" << std::endl; }

	// 前置インクリメント(引数なし)
	Integer& operator ++ ()
 	{
		std::cout << this << " : 前置インクリメント" << std::endl;

		++value_;
		return *this;
	}

	// 後置インクリメント(引数はダミー)
	Integer operator ++ ( int )
 	{
		std::cout << this << " : 後置インクリメント" << std::endl;

		Integer result( *this );
		++value_;
		return result;
	}

	// 変換関数
	operator int ()
	{
		std::cout << this << " : 変換関数" << std::endl;

		return value_;
	}

private:
	int value_;
};

void main()
{
	Integer	i;
	std::cout << i << std::endl;
	std::cout << ++i << std::endl;
	std::cout << i++ << std::endl;
	std::cout << i << std::endl;
}


この実行結果は(bcc32)この様になる。

F:\_sample2>opeSample3
0012FF88 : コンストラクタ
0012FF88 : 変換関数
0
0012FF88 : 前置インクリメント
0012FF88 : 変換関数
1
0012FF88 : 後置インクリメント
0012FF38 : コピーコンストラクタ
0012FF84 : コピーコンストラクタ
0012FF38 : デストラクタ
0012FF84 : 変換関数
1
0012FF84 : デストラクタ
0012FF88 : 変換関数
2
0012FF88 : デストラクタ


後置インクリメントは、演算前の値を返すので、演算する前に戻り値をコピーしておく。
さらに一時オブジェクトを返すので、返却先でもコピーが発生する。
要はオブジェクトの生成が2発。
当然、オブジェクトの生成が行われると、デストラクトもされるので、デストラクタが2発。


逆に前置インクリメントは、演算結果を返す上に、結果を左辺値にすることもできるので、戻り値は参照になる。
そうすると、オブジェクトの生成は一度も発生しない。


このことから、インクリメントは理由がない限り前置を使うようにしている。
細かい話なんだけど、自分が注意していること&意外に気にしない人が多いので紹介。