イテレータ..なぜそれらを使用するのですか? [複製]
質問
この質問にはすでに回答があります:
- 配列インデックスの代わりに反復子を使用する理由 25の答え
STLライブラリでは、一部のコンテナにイテレータがあり、一般的にループは単純なforループではなく、これらのコンテナを反復する優れた方法であると考えられています。
for ( int i=0; i < vecVector.size(); i++ )
{
..
}
イテレータを使用する理由とケースを教えてください。どのような場合に上記のコードスニペットを教えてください。
解決
ベクターの通常の実装では、<!> quot; int <!> quot;を使用しないことに注意してください。インデックス/サイズのタイプとして。したがって、コードは少なくともコンパイラーの警告を引き起こします。
Genericity
イテレータはコードの汎用性を高めます。
例:
typedef std::vector<int> Container ;
void doSomething(Container & p_aC)
{
for(Container::iterator it = p_aC.begin(), itEnd = p_aC.end(); it != itEnd; ++it)
{
int & i = *it ; // i is now a reference to the value iterated
// do something with "i"
}
}
ここで、ベクトルをリストに変更することを想像してみましょう(あなたのケースでは、リストの方が優れているからです)。 typedef宣言を変更し、コードを再コンパイルするだけです。
代わりにインデックスベースのコードを使用した場合、書き直す必要があります。
アクセス
イテレータは、一種のスーパーポインタのように表示する必要があります。 それは<!> quot; points <!> quot;値(または、マップの場合は、キー/値のペア)。
しかし、コンテナ内の次のアイテムに移動するメソッドがあります。または前。コンテナによっては、ランダムアクセス(ベクトルとdeque)も提供されています。
アルゴリズム
ほとんどのSTLアルゴリズムは、反復子または反復子の範囲で機能します(これも汎用性のため)。ここでは、インデックスを使用できません。
他のヒント
イテレータを使用すると、コンテナの実装についてコードに依存しなくなります。コンテナへのランダムアクセスが安価であれば、パフォーマンス面で大きな違いはありません。
しかし、多くの場合、そうであるかどうかはわかりません。リンクされたリストでメソッドを使用しようとすると、たとえば、添え字を使用すると、コンテナは要素を見つけるために反復ごとにリストを歩く必要があります。
したがって、コンテナーへのランダムアクセスが安価であることが確実でない限り、イテレーターを使用します。
イテレータを関数の引数として使用する場合、<!> quot; container <!> quot;の型から切り離すことができます。中古。たとえば、関数の結果をベクトルではなくコンソール出力に向けることができます(以下の例)。このトリックは、クラス間のカップリングを減らすのに非常に強力です。疎結合クラスはテストがはるかに簡単です。
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
template <typename InputIterator, typename OutputIterator>
void AddOne(InputIterator begin, InputIterator end, OutputIterator dest)
{
while (begin != end)
{
*dest = *begin + 1;
++dest;
++begin;
}
}
int _tmain(int argc, _TCHAR* argv[])
{
vector<int> data;
data.push_back(1);
data.push_back(2);
data.push_back(3);
// Compute intermediate results vector and dump to console
vector<int> results;
AddOne(data.begin(), data.end(), back_inserter(results));
copy(results.begin(), results.end(), ostream_iterator<int>(cout, " "));
cout << endl;
// Compute results and send directly to console, no intermediate vector required
AddOne(data.begin(), data.end(), ostream_iterator<int>(cout, " "));
cout << endl;
return 0;
}
この例では、vecVector.size()の呼び出しは、イテレーターを使用するよりも効率的ではありません。反復子は基本的に、反復されるコンテナのサイズを心配する必要がないようにカプセル化します。さらに、イテレータは必ずしも順番に並べる必要はありません。単に、適切と思われる方法で.next呼び出しに応答する必要があります。
まあ、一つには、そのベクトルをリストに変換すると、上記は機能しなくなります。
イテレータを使用すると、作業するコンテナのタイプを知る必要のない関数テンプレートを作成できます。次のこともできます。
#include <algorithm>
void printvalue(double s)
{
// Do something with s
}
int _tmain(int argc, _TCHAR* argv[])
{
double s[20] = {0};
std::for_each(s, s+20, printvalue);
return 0;
}
これは、標準ポインタがfor_eachの有効なイテレータでもあるためです。
デイブ
反復子は、ほとんどの場合、より高いレベルの抽象化です。
スニペットは、コンテナにインデックスを付けることができると想定しています。これは、std::vector<>
およびその他のコンテナ(raw配列など)に当てはまります。
ただし、std::set<>
には完全にインデックスが作成されておらず、std::map<>
のインデックス演算子は、指定された引数をマップに挿入します。for
ループで予期される動作ではありません。
また、パフォーマンスの問題は、測定および証明された場合にのみ問題になります。