私はC++言語が初めてです。私はベクトルを使い始めていて、私はインデックスを介してベクトルを反復するように見えるすべてのコードにおいて、for
ループの最初のパラメータは常にベクトルに基づくものであることに気づきました。 Javaでは、ArrayListを使って次のようにすることができます。
for(int i=0; i < vector.size(); i++){
vector[i].doSomething();
}
C++でこれが見られない理由はありますか?それは悪い習慣ですか?
C++でこれが見られない理由はありますか?それは悪い習慣ですか?
いいえ、それは悪い習慣ではありませんが、それはあなたのコードに確実な柔軟性を与えます。
通常、C++ 11より前のバージョンでは、コンテナ要素を反復処理するためのコードでは、反復子を使用しています。
std::vector<int>::iterator it = vector.begin();
これはコードがより柔軟になるためです。
すべての標準ライブラリコンテナはイテレータをサポートおよび提供しており、開発の後期段階で別のコンテナを切り替える必要がある場合は、このコードを変更する必要はありません。
注:すべての標準ライブラリコンテナで動作するコードを書くことは、見かけほど簡単にはできません。
私がiterator
スタイルのコードではなくあなたの言及した方法を使用するコードの多くを見たので、あなたがそのような習慣を見ない理由は非常に主観的で、明確な答えを持つことができません。
以下は、人々がvector.size()
のループ方法を考えていない理由です。
size()
を呼び出すのは妄想です。しかし、それは問題ではないか、それとも簡単に解決することができますfor
ループ自体よりもstd::for_each()
を優先するstd::vector
から他のコンテナ(例えばmap
、list
)に変更することは、すべてのコンテナがsize()
スタイルのループをサポートするわけではないので、ループメカニズムの変更も要求します。C++ 11はコンテナを移動するための優れた機能を提供します。これは「範囲ベースのループ」(またはJavaでは「拡張forループ」)と呼ばれます。
少しのコードで、あなたは完全な(必須!)std::vector
を通り抜けることができます。
vector<int> vi;
...
for(int i : vi)
cout << "i = " << i << endl;
ベクトルを反復処理する最も簡単な方法は、反復子を使用することです。
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
関数が定義されているすべての型に対して有効です。こちらを参照してください。 レンジベースはプレーン配列に対してどのように機能しますか?
その正しい方法は次のとおりです。
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からstd :: list、またはstd :: setに移動した場合は、数値を使って自分の値を取得することはできません。イテレータを使用することはまだ有効です。
ループの途中でコンテナを変更した場合、次にイテレータを使用するときに無効なイテレータ例外がスローされます。
STLでは、イテレータはすべての標準コンテナに実装されている抽象的な概念であるため、プログラマはコンテナを移動するためにiterators
を使用します。たとえば、std::list
にはoperator []
がまったくありません。
整数インデックスを持つ配列を反復処理することで、間違ったインデックスを持つ配列を添字にすることで、誤ったコードを書くのが簡単になるとは誰も言わなかった。たとえば、インデックスとしてi
とj
を使用してループを入れ子にした場合、配列をj
ではなくi
で誤って添字を付けて、プログラムにエラーが発生する可能性があります。
これとは対照的に、ここにリストされている他の形式、すなわち範囲ベースのfor
ループ、およびイテレータはエラーが少なくなります。言語のセマンティクスとコンパイラの型チェックメカニズムにより、誤ったインデックスを使って誤って配列にアクセスすることを防ぐことができます。