4

次の質問が頭をよぎりました。C++ stl イテレータの場合、一般的な方法は次のようなものです。

for (iterator it=obj.begin(); it!=obj.end(); it++)

私が疑問に思っているのは、実際には obj.begin() が停止するタイミングを「それ」に伝えることができ、for ループが次のようになることです。

for (iterator it=obj.begin(); !it.end(); it++)

利点は、反復子をより自己完結型にすることであり、コンテナー クラスに (iterator end()) を保存できます。

4

7 に答える 7

7

コンテナーの内容全体を反復処理する以外のことをしたい場合があります。たとえば、コンテナーの前半だけを反復処理する反復子のペアを作成できます。そのため、エンドを表す別のオブジェクトを使用すると、ユーザーは範囲のエンドをどこに配置するかをより柔軟に制御できるため、より柔軟になります。

すべてを反復する最も一般的なケースでは、多少不便であることは間違いありません。ただし、C++11 は範囲ベースの for ループを提供し、コンテナー全体のループを非常に簡単にします。プログラミングの多くのことと同様に、意図を最もよく表現するために適切な構造を選択するだけです。

于 2012-04-27T21:56:42.907 に答える
4

イテレータ API がそのように設計されている場合、ポインタは有効なイテレータではありません (ポインタには明らかにend()) メソッドがないためです。したがって、連続したメモリを持つデータ構造のイテレータを実装する最も簡単な方法は除外されます。

于 2012-04-27T21:53:06.590 に答える
2

一部のライブラリは、説明どおりに機能する「Javaスタイルのイテレータ」を提供します。

ただし、このスキーム(hasNext()など)の大きな問題は、そのようなイテレータがクラスであるということです。

たとえば、STLには、、、、などの関数を含むヘッダーが含ま<algorithm>れています。これらのルーチンはすべて、開始/終了スタイルのイテレータを使用します。その結果、これらの関数でポインターを使用できます。これらの関数がクラスであるイテレータで動作している場合(つまり、内部ではなく呼び出されている場合)、std::sortなどにポインタを渡すことはできません。このシナリオでは、すべてをクラスにラップする必要があり、時間を無駄にします。アクセスを失うことは、イテレータクラスにメソッドを追加することで得られる小さな便利さの価値はありません。これが、イテレータがと比較される理由です。std::copystd::generatestd::sortstd::lower_boundstd::fillhasNext()!= end()<algorithm>atEnd()end()

于 2012-04-27T22:04:32.363 に答える
1

イテレータがコンテナについて「知っている」必要はありません。コンテナーを包含することについて何も知らなくても、メモリのブロックまたは現在のノード (または反復されるデータ構造に適したもの) のオフセットを認識して気にすることができます。たとえば、vector反復子は、ベクトルの終了位置を (それ自体で) 知らなくても、単純なポインターとして実装できます。

また、STL アルゴリズムは生のポインターでも機能する必要があるため、反復子はポインターを「模倣」する必要があります。

于 2012-04-27T21:54:54.207 に答える
1

最終的には、STL が (90 年代初頭に Stepanov によって) 発明され、後に C++ 標準化プロセスで承認されたときに、反復子がポインターの一般化になるという決定が下されました。http://www.sgi.com/tech/stl/stl_introduction.htmlから:

C 配列を逆にする例では、reverse の引数は明らかに型double*です。ただし、ベクトルまたはリストを逆にする場合、逆にする引数は何ですか? ... 答えは、reverse の引数は、ポインタの一般化であるイテレータです。

イテレータはポインタの一般化である必要はありませんでした。firstC++ 標準ライブラリ (およびそれ以前の STL) は、理論的には異なる反復モデルを使用できました。このモデルでは、反復は、反復子とのペアではなく、単一の反復子オブジェクトまたは単一の範囲オブジェクトのいずれかで表されますlast

性能差はあまりないと思います。確かに、最新の C++ コンパイラにはありません。STL (およびそれに基づく標準ライブラリ) は常に、コンパイラによる適切なインライン展開にパフォーマンスを依存しており、これらのクラスは、コンパイラがコンテナーの内部で既に処理しなければならないものよりも悪くはありません。また、ポインターのペアを反復子または範囲オブジェクトに変換する単純なラッパーを提供することも、大きな問題にはなりません。

他のイテレータ モデルを好む人もいます。James Gosling (または、Java イテレータを設計した人物) です。また、範囲を好む人もいます (多くの C++ プログラマーを含む: したがって Boost.Range)。

STL と C++ の作成者は、STL スタイルのイテレータが C とのある種の互換性を保持しているという事実、つまりポインタを使用して配列 (またはユーザーがポインターのペア)、ほとんど変更せずに、反復子を使用してコンテナー (または他の範囲) を操作する C++ アルゴリズムに変換します。これは、新しい言語が既存のユーザー ベースにより容易に同化できることを意味する考え方です。これは、初期の C++ の目標の 1 つでした。

そのため、「ポインタをイテレータではなく、イテレータを大きくすることを犠牲にして、数文字短い end をテストする手段を提供できないか」などの質問は、おそらく当時はあまり関心がなかったでしょう。たとえば、Andrei Alexandrescu は、彼の意見では、これはもはや最良の選択ではなく、範囲は反復子よりも優れているとしばらくの間言い続けてきました。

于 2012-04-28T00:01:15.757 に答える
0

イテレータのbeginand end(and rbeginand rend) がどこにあるかを知ることは確かに便利ですが、あなたが述べたケースは、C++11 の範囲ベースのfor-loopによって多少処理されます。

for (auto it: obj)
{
    // Do something with *it;
}

ボイラープレートをあまり書かなくても、C++11 で物事を成し遂げる方がはるかに簡単です!

于 2012-04-27T22:52:59.013 に答える
0

イテレータをそのように動作させることで、部分範囲を柔軟に処理できます。begin()常に で開始または終了する必要はありませんend()。たとえば、特定の値を含むすべての反復子を操作する場合はequal_range()、開始と終了に の戻り値を使用できます。

D 言語にはrange、反復子範囲の開始と終了の両方を組み込む があります。C++ についても提案されました: http://groups.google.com/group/comp.lang.c++.moderated/browse_thread/thread/7e52451ed8eea31c

于 2012-04-27T21:55:28.633 に答える