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


139

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

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

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


1
forループは関数ではないため、パラメーター(または引数、渡したもの)はありません。のような意味ですかstd::vector<int>::size_type i = 0;、それともおそらくstd::vector<int>::iterator it = vector.begin();
chris

まさに、私が目にするすべての例はそのように書かれています。
Flynn

4
Javaでは、for-eachループを使用するか、イテレータを使用します。構文は少し異なりますが、C ++とほとんど同じです。
Jesse Good


10
ここでの答えのほとんどは、Qが誤っていると仮定していstd::vectorます。、ここで尋ねられる実際のQは次のとおりです。C++でこれが表示されない理由はありますか?それは悪い習慣ですか?別名繰り返し処理中に反復子を使用するコードが常にC ++で表示さstd::vector
Alok Save

回答:


92

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

いいえ、それは悪い習慣ではありませんが、次のアプローチはコードに一定の柔軟性を与えます

通常、C ++ 11より前のコンテナ要素を反復するコードでは、次のような反復子を使用します。

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

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

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

注:考えられるすべての標準ライブラリコンテナで機能するコードを記述することは、思われるほど簡単ではありません。


25
この特定のケース/コードスニペットで、インデックス作成についてイテレータにアドバイスする理由を誰かに説明していただけませんか?あなたが話しているこの「柔軟性」とは何ですか?個人的には、イテレータは好きではありません。イテレータはコードを膨らませます。同じ効果を得るために入力する文字が増えるだけです。特に使用できない場合auto
バイオレットキリン2012年

8
@VioletGiraffe:イテレータを使用している間、空の範囲などの特定のケースで失敗することは難しく、コードはより冗長になります。問題または認識と選択を参照してください。したがって、無限に議論することができます。
Alok Save

9
なぜイテレータを宣言する方法だけを示し、それを使用してループを行う方法を示さないのですか...?
underscore_d

115

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

以下はvector.size()、ループの方法を考慮していない理由である可能性があります。

  1. size()ループ状態で毎回呼び出すことに偏執的である。ただし、問題ではないか、簡単に修正できる
  2. 好むstd::for_each()上でforループ自体
  3. その後からコンテナを変更するstd::vector他の1(例えばにすることは maplist必ずしもすべてのコンテナ支持ので)また、ループメカニズムの変更を要求するsize()ループのスタイル

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

少しのコードで、完全にトラバースできます(必須です!)std::vector

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

12
範囲ベースのforループの小さな欠点に注意してください#pragma omp parallel for。これをで使用することはできません。
liborm 2014年

2
読み取るコードが少ないので、コンパクトバージョンが好きです。メンタル調整を行うと、理解がはるかに簡単になり、バグがより目立ちます。また、コードのチャンクがはるかに大きいため、非標準のイテレーションが発生している場合にも、それがより明確になります。
Code Abominator 2016年

87

ベクトルを反復する最もクリーンな方法は、反復子を使用することです。

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

または(上記と同等)

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

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

これはおそらくあなたが見たものです。あなたが言及するアプローチと比較して、利点はのタイプに大きく依存しないことですvectorvector別の「コレクション型」クラスに変更しても、コードはおそらく機能します。ただし、Javaでも同様のことができます。概念的には大きな違いはありません。ただし、C ++はテンプレートを使用してこれを実装します(Javaのジェネリックと比較して)。したがって、この方法は、静的配列などの非クラス型であっても、beginおよびend関数が定義されているすべての型で機能します。こちらをご覧ください:範囲ベースのはプレーン配列でどのように機能しますか?


5
auto、free begin / endもC ++ 11です。また、多くの場合、it ++ではなく++ itを使用する必要があります。
ForEveR 2012年

はい、あなたが正しい。ただし、beginとの実装endはワンライナーです。
JohnB

@JohnB固定サイズの配列でも機能するため、1ライナーではありません。auto一方、かなりトリッキーになります。
juanchopanza

ベクトルにのみ必要な場合は、ワンライナーです。
JohnB

それでも、最初の例はC ++ 03では機能しないため、誤解を招く可能性があります。
juanchopanza

35

これを行う正しい方法は次のとおりです。

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();
}

std::vector<T>::size_typeいつもではないsize_tですか?それは私がいつも使うタイプです。
バイオレットキリン2012年

1
@VioletGiraffe私はあなたが正しい(本当にチェックされていない)と確信していますが、std :: vector <T> :: size_typeを使用することをお勧めします。
DiGMi 2012年

8

イテレータを使用する理由はいくつかありますが、その一部を以下に示します。

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

つまり、std :: vectorからstd :: list、またはstd :: setに移動する場合、数値インデックスを使用して含まれている値を取得することはできません。イテレータの使用は引き続き有効です。

無効な反復のランタイムキャッチ

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


1
上記のポイントをコード例で説明する私たちの記事/投稿を指すことができますか?素晴らしいことだ!または、1つ追加できる場合:)
Anu

5

整数のインデックスを持つ配列を反復処理することで、間違ったインデックスの配列に添え字を付けることにより、欠陥のあるコードを簡単に記述できると誰も言っていないことに驚きました。たとえば、インデックスとしてiおよびjを使用してループをネストしている場合、jではなくで配列に誤って添え字を付ける可能性がありi、プログラムに障害が発生する可能性があります。

対照的に、ここに記載されている他の形式、つまり範囲ベースのforループとイテレータは、エラーが発生しにくくなります。言語のセマンティクスとコンパイラの型チェックメカニズムにより、誤ったインデックスを使用して配列に誤ってアクセスすることが防止されます。


4

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


3

auto演算子を使用すると、データ型やベクトルやその他のデータ構造のサイズを気にする必要がないため、本当に使いやすくなります。

autoおよびforループを使用したベクトルの反復

vector<int> vec = {1,2,3,4,5}

for(auto itr : vec)
    cout << itr << " ";

出力:

1 2 3 4 5

このメソッドを使用して、セットとリストを反復することもできます。使用するオートは、自動的にテンプレートで使用されるデータ型を検出し、あなたがそれを使用することができます。だから、私たちが持っていた場合でも、vectorstringか、char同じ構文がうまく動作します


1

ループを反復してその値を出力する正しい方法は次のとおりです。

#include<vector>

//declare the vector of type int
vector<int> v;

//insert the 5 element in the vector
for ( unsigned int i = 0; i < 5; i++){
    v.push_back(i);
}

//print those element
for (auto it = 0; it < v.end(); i++){
    std::cout << *it << std::endl;
}


0
 //different declaration type
    vector<int>v;  
    vector<int>v2(5,30); //size is 5 and fill up with 30
    vector<int>v3={10,20,30};
    
    //From C++11 and onwards
    for(auto itr:v2)
        cout<<"\n"<<itr;
     
     //(pre c++11)   
    for(auto itr=v3.begin(); itr !=v3.end(); itr++)
        cout<<"\n"<<*itr;
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.