web-dev-qa-db-ja.com

forループを使用してC ++ベクトルを反復処理する

私はC++言語が初めてです。私はベクトルを使い始めていて、私はインデックスを介してベクトルを反復するように見えるすべてのコードにおいて、forループの最初のパラメータは常にベクトルに基づくものであることに気づきました。 Javaでは、ArrayListを使って次のようにすることができます。

for(int i=0; i < vector.size(); i++){
   vector[i].doSomething();
}

C++でこれが見られない理由はありますか?それは悪い習慣ですか?

108
Flynn

C++でこれが見られない理由はありますか?それは悪い習慣ですか?

いいえ、それは悪い習慣ではありませんが、それはあなたのコードに確実な柔軟性を与えます。

通常、C++ 11より前のバージョンでは、コンテナ要素を反復処理するためのコードでは、反復子を使用しています。

std::vector<int>::iterator it = vector.begin();

これはコードがより柔軟になるためです。

すべての標準ライブラリコンテナはイテレータをサポートおよび提供しており、開発の後期段階で別のコンテナを切り替える必要がある場合は、このコードを変更する必要はありません。

注:すべての標準ライブラリコンテナで動作するコードを書くことは、見かけほど簡単にはできません。

77
Alok Save

私がiteratorスタイルのコードではなくあなたの言及した方法を使用するコードの多くを見たので、あなたがそのような習慣を見ない理由は非常に主観的で、明確な答えを持つことができません。

以下は、人々がvector.size()のループ方法を考えていない理由です。

  1. ループ状態の中で毎回size()を呼び出すのは妄想です。しかし、それは問題ではないか、それとも簡単に解決することができます
  2. forループ自体よりもstd::for_each()を優先する
  3. コンテナをstd::vectorから他のコンテナ(例えばmaplist)に変更することは、すべてのコンテナがsize()スタイルのループをサポートするわけではないので、ループメカニズムの変更も要求します。

C++ 11はコンテナを移動するための優れた機能を提供します。これは「範囲ベースのループ」(またはJavaでは「拡張forループ」)と呼ばれます。

少しのコードで、あなたは完全な(必須!)std::vectorを通り抜けることができます。

vector<int> vi;
...
for(int i : vi) 
  cout << "i = " << i << endl;
87
iammilind

ベクトルを反復処理する最も簡単な方法は、反復子を使用することです。

for (auto it = begin (vector); it != end (vector); ++it) {
    it->doSomething ();
}

または(上記と同じ)

for (auto & element : vector) {
    element.doSomething ();
}

C++ 0xより前では、autoをイテレータ型に置き換えて、グローバル関数beginとendの代わりにメンバ関数を使用する必要があります。

これはおそらくあなたが見たことです。あなたが言及したアプローチと比較して、利点はあなたがvectorの型に強く依存しないということです。 vectorを別の "collection-type"クラスに変更しても、おそらくコードは機能するでしょう。ただし、Javaでも同様のことができます。概念的にはそれほど違いはありません。ただし、C++ではこれを実装するためにテンプレートを使用します(Javaの総称と比較して)。したがって、静的配列のようなクラス以外の型であっても、このアプローチはbeginおよびend関数が定義されているすべての型に対して有効です。こちらを参照してください。 レンジベースはプレーン配列に対してどのように機能しますか?

69
JohnB

その正しい方法は次のとおりです。

for(std::vector<T>::iterator it = v.begin(); it != v.end(); ++it) {
    it->doSomething();
 }

ここで、Tはベクトル内のクラスの型です。たとえば、クラスがCActivityの場合は、Tの代わりにCActivityを書くだけです。

この種のメソッドはすべてのSTLで機能します(ベクトルだけでなく、少し優れています)。

それでもインデックスを使いたい場合は、次のようにします。

for(std::vector<T>::size_type i = 0; i != v.size(); i++) {
    v[i].doSomething();
}
32
DiGMi

イテレータを使用する理由はいくつかあります。そのいくつかを次に示します。

後でコンテナを切り替えてもコードは無効になりません。

つまり、std :: vectorからstd :: list、またはstd :: setに移動した場合は、数値を使って自分の値を取得することはできません。イテレータを使用することはまだ有効です。

無効な反復の実行時キャッチ

ループの途中でコンテナを変更した場合、次にイテレータを使用するときに無効なイテレータ例外がスローされます。

7
Eddie Parker

STLでは、イテレータはすべての標準コンテナに実装されている抽象的な概念であるため、プログラマはコンテナを移動するためにiteratorsを使用します。たとえば、std::listにはoperator []がまったくありません。

4
ForEveR

整数インデックスを持つ配列を反復処理することで、間違ったインデックスを持つ配列を添字にすることで、誤ったコードを書くのが簡単になるとは誰も言わなかった。たとえば、インデックスとしてijを使用してループを入れ子にした場合、配列をjではなくiで誤って添字を付けて、プログラムにエラーが発生する可能性があります。

これとは対照的に、ここにリストされている他の形式、すなわち範囲ベースのforループ、およびイテレータはエラーが少なくなります。言語のセマンティクスとコンパイラの型チェックメカニズムにより、誤ったインデックスを使って誤って配列にアクセスすることを防ぐことができます。

3