多くの現代言語とは異なり、プレーンC++配列には関数がありません.size()
。ストレージの種類に応じて、リストを反復処理するためのオプションがいくつかあります。
ストレージの一般的なオプションは次のとおりです。
// used for fixed size storage. Requires #include <array>
std::array<type, size> collection;
// used for dynamic sized storage. Requires #include <vector>
std::vector<type> collection;
// Dynamic storage. In general: slower iteration, faster insert
// Requires #include <list>
std::list<type> collection;
// Old style C arrays
int myarray[size];
反復のオプションは、使用しているタイプによって異なります。単純な古いC配列を使用している場合は、サイズを別の場所に格納するか、配列の型のサイズに基づいて配列のサイズを計算できます。配列のサイズを計算することには、DevSolarによるこの回答で概説されているいくつかの欠点があります
// Store the value as a constant
int oldschool[10];
for(int i = 0; i < 10; ++i) {
oldschool[i]; // Get
oldschool[i] = 5; // Set
}
// Calculate the size of the array
int size = sizeof(oldschool)/sizeof(int);
for(int i = 0; i < size; ++i) {
oldschool[i]; // Get
oldschool[i] = 5; // Set
}
.begin()
および関数を提供する任意の型を使用している場合は、.end()
それらを使用して、インデックスベースの反復と比較してC++で適切なスタイルと見なされるイテレータを取得できます。
// Could also be an array, list, or anything with begin()/end()
std::vector<int> newschool;
// Regular iterator, non-C++11
for(std::vector<int>::iterator num = newschool.begin(); num != newschool.end(); ++num) {
int current = *num; // * gets the number out of the iterator
*num = 5; // Sets the number.
}
// Better syntax, use auto! automatically gets the right iterator type (C++11)
for(auto num = newschool.begin(); num != newschool.end(); ++num) {
int current = *num; // As above
*num = 5;
}
// std::for_each also available
std::for_each(newschool.begin(), newschool.end(), function_taking_int);
// std::for_each with lambdas (C++11)
std::for_each(newschool.begin(), newschool.end(), [](int i) {
// Just use i, can't modify though.
});
ベクトルは、配列のドロップイン置換として設計されているため、特別です。関数を使用して配列を反復処理するのとまったく同じように、ベクトルを反復処理でき.size()
ます。ただし、これはC ++では悪い習慣と見なされており、可能な場合はイテレータを使用することをお勧めします。
std::vector<int> badpractice;
for(int i = 0; i < badpractice.size(); ++i) {
badpractice[i]; // Get
badpractice[i] = 5; // Set
}
.begin()
C ++ 11(新しい標準)は、それに基づいた新しいファンシーな範囲ももたらします。これは、およびを提供するすべてのタイプで機能するはず.end()
です。ただし、コンパイラのサポートはこの機能によって異なる場合があります。begin(type)
代わりにとを使用することもできますend(type)
。
std::array<int, 10> fancy;
for(int i : fancy) {
// Just use i, can't modify though.
}
// begin/end requires #include <iterator> also included in most container headers.
for(auto num = std::begin(fancy); num != std::end(fancy); ++num) {
int current = *num; // Get
*num = 131; // Set
}
std::begin
また、もう1つの興味深い特性があります。それは、生の配列で機能します。これは、配列と非配列の間で同じ反復セマンティクスを使用できることを意味します(生の配列よりも標準タイプを優先する必要があります)。
int raw[10];
for(auto num = std::begin(raw); num != std::end(raw); ++num) {
int current = *num; // Get
*num = 131; // Set
}
container.erase()
呼び出し中に既存のすべてのイテレータが無効になるため、ループ中にコレクションからアイテムを削除する場合にも注意する必要があります。
std::vector<int> numbers;
for(auto num = numbers.begin(); num != numbers.end(); /* Intentionally empty */) {
...
if(someDeleteCondition) {
num = numbers.erase(num);
} else {
// No deletition, no problem
++num;
}
}
このリストは包括的ではありませんが、ご覧のとおり、コレクションを反復処理する方法はたくさんあります。一般に、他の方法で行う正当な理由がない限り、イテレータを優先します。