*C++ワンポイントレッスン Lesson-1  i++ と ++i の違い [#mad17e5b]

**前置きと後置きの演算子 [#sbc8cbac]
-C++には、C言語と同様の変数をインクリメント・デクリメントする演算子(++、--)がある。

-C言語の時は、おもに整数,ポインタ変数などに対し、アセンブラ命令のインクリメント・デクリメントに相当する命令を直接記述することにより高速で効率の良いコード生成を可能としてきた。

-C++では、オブジェクトの演算子をユーザーが定義できるようになったため、演算子の持つ特性と役割が大きく拡張された。

**前置き++と後置き++の違い <レジスタ変数時> [#h5bea32b]
-C言語Cの時と同様に、レジスタ変数における前置きと後置きのコード生成を考える。

 C++での記述
 if (i++ < 10) Hoge(i)
 
 生成されるアセンブラ
 mov ebx, eax
 inc eax
 cmp ebx, 10
 jnc L1
 push eax
 call Hoge
 L1: 

 C++での記述
 if (++i < 11) Hoge(i);
 
 生成されるアセンブラ
 inc eax
 cmp eax, 11
 jnc L1
 push eax
 call Hoge
 L1: 

***前置き++と後置き++の違い <レジスタ変数時> 結果 [#a1e17196]
-前置き++の場合のデメリット

--命令が1つされる。(メモリの増加、速度の低下)
--レジスタ変数が1つ消費される。(最適化の効率低下)
--コンパイル時間の増加。(最適化の余地が多いため、最適化ルーチンのコストが増加する)

※実際には、この程度のコードならほとんどのコンパイラは最適化が可能なので、実行時の差異は生じません。~
※生成されるアセンブラはイメージです。実際にはコンパイラによって生成されるコードは異なります。~

**前置き++と後置き++の違い <クラスオブジェクト時> [#gb7a7472]
-クラスオブジェクトでオーバーライドされた演算子における前置きと後置きのコード生成を考える。

 C++での記述
 CFoo foo;
 if (foo++ < 10) Hoge(foo)
 
 インライン展開されるコード
 CFoo tmp = foo;
 foo.m_value.plus1();
 if (CFoo::operator < (tmp.m_value, 10))
 {
   Hoge(foo)
 }

 C++での記述
 CFoo foo;
 if (++foo < 11) Hoge(foo);
 
 インライン展開されるコード
 if (CFoo::operator < (foo.m_value.plus1() , 11)) 
 {
   Hoge(foo)
 }

***前置き++と後置き++の違い <クラスオブジェクト時> 実装 [#paa00d8b]
CFooの実装は以下のようになる。

 struct CValue {
   CValue & plus1();
 }
 bool operator < (const CValue&, int);
 
 struct CFoo {
   CValue m_value;
   CFoo() {}
   bool operator < (int n) { return m_value  < n; }       
   CFoo& operator ++() {
     m_value .plus1();
     return *this; 
   }
   CFoo operator ++(int) {
     CFoo tmp = *this;   //<- オブジェクトの生成とコピーが発生
     m_value .plus1();
     return tmp;         //<- オブジェクトの生成とコピーが発生
   }
 }


***前置き++と後置き++の違い <クラスオブジェクト時> 結果 [#xd903d79]
-前置き++時のデメリット

--CFooのオブジェクトの生成の追加
--CFooオブジェクトの代入(コピー)の追加(インライン展開されない場合は2回発生する)
--コード量の増加
--最適化コストの増加

**前置き++と後置き++のパフォーマンス測定 [#q5ce78b8]

STLのvector<int>とmap<int,int>のiteratorをインクリメント(++)する実行時間を、前置きと後置きで比較してみました。

|オブジェクト|map<> iterator++|map<> ++iterator|vector<> iterator++|vector<> ++iterator|h
|実行時間(Releaseビルド)|1.300815|0.300459|0.567246|0.566541|
|比率|100.00%|23.10%|100.00%|99.98%|
|実行時間(Debugビルド)|1.361722|0.108110|1.203101|0.157290|
|比率|100.0%|7.94%|100.00%|13.07%|

- vector<int>::iteratorの場合は、int*としてコンパイルされるため全く差は出ませんが、map<int,int>::iteratorの場合は4倍以上の速度差が出ています。
- 最適化が行われないデバッグビルドにおいては、map<int,int>::iteratorは12倍以上、vector<int>でも7倍以上、前置き++のほうが高速になっています。
- つまり、最適化が効きにくい複雑なプログラムほど、後置++のロスは大きくなることが予想されます。

&size(15){''この結果を見ても、意味のない i++ を書きますか?''};

#back


※測定環境  Athlon64 3.0GHz x2 VisualStudio 2009



[[ssacontents]]

トップ   新規 一覧 単語検索 最終更新   ヘルプ   最終更新のRSS