2

ループの効率を上げるために頭の後ろに座っていたものを解決するには、スタックオーバーフローの意見が必要です。さて、プログラミングを始めたとき、「それを機能させる」ことが大きな優先事項であり、私のforループのほとんどはこのように見えました。

for (int i = 0; i < N; i++) {;}

それから私は、特に事前インクリメントによっていくつかの呼び出しを節約するC ++開発で、それがN呼び出しであり、私が習慣にした読みやすさを変えないことを考慮して考えられました。

for (int i = 0; i < N; ++i) {;}

これはしばらくの間は十分でしたが、読みやすさに焦点を当て、SteveMcConnellによるCodeCompleteの一部を読んだ後、私はこれに到達しました。

for (int loop_index = 0; loop_index < loop_count; ++loop_index) {;}

そして、これらの変数はコードのコンテキストに基づいて変化します。次に、組み込み型のコンストラクターと割り当てに関する効果的なC++のいくつかを読みました。そして基本的にその違いは

int i = 42;

int i(42); 

前者はコンストラクターと代入演算子を呼び出し、後者はコンストラクターのみを呼び出すということです。だから私はコーディング中にそれを私のルーチンに取り入れました。だから私の質問は、これがforループを書くための最も効率的で読みやすい方法ですか?

for (int loop_index(0); loop_index < loop_counter; ++loop_index) {;}
4

6 に答える 6

5

実際、

for (int i = 0; i < N; i++) {;}

結構です。iは であるintため、コンパイラはとにかくポストインクリメントを最適化します。intwithint i(42);または withを初期化するかどうかint i = 42;も好みの問題です。

iの代わりにiterator も呼び出しますloop_index。前者はいたるところで理解されていますが、後者は奇妙です。

ただし、反復子を扱っている場合は状況が変わります。に最適化される可能性はit++まだあります++itが、ここでは事前インクリメントを使用したいと思います。

于 2012-08-08T13:35:49.753 に答える
2

イテレータを使用していない場合は、コンパイラがこれを同じコードに変換するため、問題にはなりません。

補遺: イテレータを使用している場合:

for(vector<int>::iterator i = a.begin(); i != a.end(); ++i)

++i は i.operator++() であり、i++ は i.operator++(int) であり、これは 2 つの異なることを意味します。

ところで。これが問題である場合は、パフォーマンスを測定できます。

于 2012-08-08T13:35:35.377 に答える
2

まず第一に、あなたの最後のポイントは正しくありません。この行

int i = 42;

初期化をコピーします (この関連するGoTWを参照してください)。そこに課題はありません。割り当てには、必要になります

int i;
i = 42;

第 2 に、整数型などのビルトインを扱う場合、コンパイラによって生成されるコードにはおそらく違いはありません。さまざまなオプションをプロファイリングして確認する必要があります。

于 2012-08-08T13:35:40.590 に答える
1

組み込み型またはほとんどの反復子を使用している場合、これらはすべて同じコードにコンパイルされます。

したがって、C++ にとって最も慣用的なものに固執します。

for (int i = 0; i < N; ++i) {;}

または、冗長になりたい場合(ただし、 i と k は伝統的にループインデックスです)

for (int loop_index = 0; loop_index < loop_count; ++loop_index) {;}

私の最初の主張の証拠として: これら 2 つのコードは、同一のアセンブリ コードを生成します。(でコンパイルされた OS X 上の g++ 4.0.1 g++ -Os -S test.cpp)

void f(int); // So that smart compilers can't optimise away the loop entirely.

void g()
{
  int N=100;
  // for( int i=0; i<N; i++)
  for (int i(0); i < N; ++i) 
  {
    f(i);
  }
}

完全を期すために、生成されたコードは次のとおりです。

.globl __Z1gv
__Z1gv:
LFB2:
    pushl   %ebp
LCFI0:
    movl    %esp, %ebp
LCFI1:
    pushl   %esi
LCFI2:
    xorl    %esi, %esi
    subl    $20, %esp
LCFI3:
L2:
    movl    %esi, (%esp)
    incl    %esi
    call    L__Z1fi$stub
    cmpl    $100, %esi
    jne L2
    addl    $20, %esp
    popl    %esi
    leave
    ret
于 2012-08-08T13:34:43.520 に答える
0

効率的?いいえ、ほとんどの場合同じです。ほとんどのコンパイラは、intおよびsimpleポインタのポストインクリメントと初期化を最適化するためです。また、loop_indexがi、IMOよりも読みやすいかどうかについては、単純なカウンターでは読みにくくなっています。

ただし、イテレータの場合は別の話です。コンパイラは、オーバーロードされたポストインクリメントまたはイテレータの二重初期化を常に最適化できるとは限りません。また、イテレータに反復オブジェクトの名前を使用することをお勧めします。これは、より適切に使用されているものを反映しているため、矢印->演算子を使用してイテレータを逆参照すると、オブジェクトの名前を使用する方が自然に見えます。

于 2012-08-08T13:52:55.307 に答える
0

int i他の人はポスト インクリメントとプリ インクリメントと初期化を非常によくカバーしていますが、ほとんどの場合、 using は よりも読みやすいことを追加したいと思いint loop_indexます。

他の名前で呼びたいと思う唯一のケースは、複数のループがあり、ループ インデックス間の複雑な相互作用がある場合です。例えば

for (int row = 0; row < rows; row++) {
    for (int column = 0; column < columns; column++) {
        stuff[row * columns + column] = calc_stuff(row, column);
    }
}
于 2012-08-08T13:44:12.713 に答える