[C++] std::vectorをfor文で反復処理する方法
C++のstd::vector
をfor
文で反復処理する方法は、インデックスを使用する方法とイテレータを使用する方法の2つがあります。
インデックスを使う場合はfor (size_t i = 0; i < vec.size(); ++i)
の形式で、vec[i]
で要素にアクセスします。
一方、イテレータを使う場合はfor (auto it = vec.begin(); it != vec.end(); ++it)
の形式で、*it
で要素を参照します。
C++11以降では範囲ベースfor
文も利用可能で、for (auto& elem : vec)
の形式で簡潔に記述できます。
std::vectorとは
std::vector
は、C++の標準ライブラリに含まれる動的配列の一種です。
配列のサイズを動的に変更できるため、要素の追加や削除が容易です。
以下に、std::vector
の主な特徴を示します。
特徴 | 説明 |
---|---|
動的サイズ | 要素数に応じて自動的にサイズが変更される |
ランダムアクセス | インデックスを使用して要素にアクセス可能 |
メモリ管理 | 自動的にメモリを管理し、不要になったメモリを解放 |
STLとの互換性 | 他のSTLコンテナやアルゴリズムと連携可能 |
std::vector
は、特に要素数が不明な場合や、頻繁に要素の追加・削除が行われる場合に非常に便利です。
次のセクションでは、std::vector
を使用したfor文での反復処理について詳しく解説します。
for文を使った反復処理の基本
std::vector
の要素を反復処理するためには、通常のfor文を使用することができます。
基本的な構文は以下の通りです。
#include <iostream>
#include <vector>
int main() {
std::vector<int> numbers = {1, 2, 3, 4, 5}; // 整数のベクターを初期化
// for文を使って反復処理
for (size_t i = 0; i < numbers.size(); i++) { // ベクターのサイズ分ループ
std::cout << numbers[i] << std::endl; // 各要素を出力
}
return 0;
}
1
2
3
4
5
このコードでは、std::vector<int>
型のnumbers
というベクターを作成し、1から5までの整数を格納しています。
for文を使用して、インデックスi
を使って各要素にアクセスし、出力しています。
numbers.size()
でベクターのサイズを取得し、ループの条件として使用しています。
これにより、すべての要素を順番に処理することができます。
インデックスを使用した反復処理
std::vector
の要素にアクセスする際、インデックスを使用する方法は非常に一般的です。
インデックスを使うことで、特定の位置にある要素を直接参照することができます。
以下に、インデックスを使用した反復処理の例を示します。
#include <iostream>
#include <vector>
int main() {
std::vector<std::string> fruits = {"りんご", "バナナ", "オレンジ", "ぶどう", "いちご"}; // 文字列のベクターを初期化
// インデックスを使用して反復処理
for (size_t i = 0; i < fruits.size(); i++) { // ベクターのサイズ分ループ
std::cout << "フルーツ " << (i + 1) << ": " << fruits[i] << std::endl; // 各要素を出力
}
return 0;
}
フルーツ 1: りんご
フルーツ 2: バナナ
フルーツ 3: オレンジ
フルーツ 4: ぶどう
フルーツ 5: いちご
このコードでは、std::vector<std::string>
型のfruits
というベクターを作成し、いくつかのフルーツ名を格納しています。
for文を使用して、インデックスi
を使って各要素にアクセスし、出力しています。
インデックスを使うことで、要素の位置を明示的に示すことができ、特定の要素に対する操作が容易になります。
イテレータを使用した反復処理
std::vector
では、イテレータを使用して要素を反復処理することもできます。
イテレータは、コンテナ内の要素を指し示すオブジェクトで、ポインタのように振る舞います。
イテレータを使うことで、より柔軟で安全な反復処理が可能になります。
以下に、イテレータを使用した反復処理の例を示します。
#include <iostream>
#include <vector>
int main() {
std::vector<double> temperatures = {36.5, 37.0, 38.2, 36.8, 37.5}; // 温度のベクターを初期化
// イテレータを使用して反復処理
for (std::vector<double>::iterator it = temperatures.begin(); it != temperatures.end(); ++it) { // ベクターの先頭から末尾までループ
std::cout << "温度: " << *it << " °C" << std::endl; // 各要素を出力
}
return 0;
}
温度: 36.5 °C
温度: 37.0 °C
温度: 38.2 °C
温度: 36.8 °C
温度: 37.5 °C
このコードでは、std::vector<double>
型のtemperatures
というベクターを作成し、いくつかの温度値を格納しています。
イテレータit
を使用して、temperatures.begin()
からtemperatures.end()
までループし、各要素にアクセスしています。
*it
を使って、イテレータが指し示す要素の値を取得し、出力しています。
イテレータを使用することで、コンテナの種類に依存せずに同様の操作が可能になるため、コードの再利用性が向上します。
範囲ベースfor文を使用した反復処理
C++11以降、範囲ベースfor文を使用することで、std::vector
の要素を簡潔に反復処理することができます。
この構文は、イテレータを明示的に使用することなく、コンテナ内のすべての要素にアクセスできるため、コードがより読みやすくなります。
以下に、範囲ベースfor文を使用した反復処理の例を示します。
#include <iostream>
#include <vector>
int main() {
std::vector<std::string> colors = {"赤", "青", "緑", "黄", "紫"}; // 色のベクターを初期化
// 範囲ベースfor文を使用して反復処理
for (const std::string& color : colors) { // 各要素を参照で取得
std::cout << "色: " << color << std::endl; // 各要素を出力
}
return 0;
}
色: 赤
色: 青
色: 緑
色: 黄
色: 紫
このコードでは、std::vector<std::string>
型のcolors
というベクターを作成し、いくつかの色名を格納しています。
範囲ベースfor文を使用して、colors
内の各要素にアクセスし、出力しています。
const std::string& color
とすることで、要素を参照で取得し、コピーを避けることができ、効率的です。
範囲ベースfor文は、特に要素数が多い場合や、コードを簡潔に保ちたい場合に非常に便利です。
各方法の比較と選択基準
std::vector
の要素を反復処理する方法には、主に以下の4つがあります。
それぞれの方法の特徴と選択基準を比較してみましょう。
方法 | 特徴 | 利点 | 欠点 |
---|---|---|---|
インデックスを使用したfor文 | インデックスを使って要素にアクセス | シンプルで直感的 | インデックスの範囲外アクセスに注意が必要 |
イテレータを使用したfor文 | イテレータを使って要素にアクセス | コンテナの種類に依存せず再利用可能 | コードがやや冗長になることがある |
範囲ベースfor文 | C++11以降の新しい構文 | 簡潔で可読性が高い | 古いコンパイラでは使用できないことがある |
選択基準
- シンプルさ: 簡単な処理や小規模なプログラムでは、インデックスを使用したfor文が適しています。
- 再利用性: 異なるコンテナを扱う場合や、STLアルゴリズムと組み合わせる場合は、イテレータを使用することが推奨されます。
- 可読性: コードの可読性を重視する場合、範囲ベースfor文が最も適しています。
特に、要素数が多い場合や、複雑な処理を行う場合に有効です。
これらの方法を理解し、状況に応じて適切な方法を選択することで、効率的で可読性の高いコードを書くことができます。
応用例:std::vectorのネスト構造の反復処理
std::vector
は、他のstd::vector
を要素として持つことができるため、ネスト構造を作成することが可能です。
このような場合、二重のfor文や範囲ベースfor文を使用して、ネストされたベクターの要素を反復処理することができます。
以下に、ネスト構造のstd::vector
を反復処理する例を示します。
#include <iostream>
#include <vector>
int main() {
// 整数のベクターのベクターを初期化
std::vector<std::vector<int>> matrix = {
{1, 2, 3},
{4, 5, 6},
{7, 8, 9}
};
// ネストされたベクターを反復処理
for (const std::vector<int>& row : matrix) { // 各行を参照で取得
for (int value : row) { // 各行の要素を反復処理
std::cout << value << " "; // 各要素を出力
}
std::cout << std::endl; // 行の終わりで改行
}
return 0;
}
1 2 3
4 5 6
7 8 9
このコードでは、std::vector<std::vector<int>>
型のmatrix
という二次元ベクターを作成し、3×3の整数行列を格納しています。
範囲ベースfor文を使用して、外側のベクター(行)を反復処理し、内側のfor文で各行の要素を反復処理しています。
これにより、ネストされた構造の要素を簡潔に出力することができます。
ネスト構造のstd::vector
を扱う際には、このような方法が非常に便利です。
まとめ
この記事では、std::vector
を使用した反復処理のさまざまな方法について解説しました。
インデックスを使用したfor文、イテレータ、範囲ベースfor文のそれぞれの特徴や利点を比較し、ネスト構造のstd::vector
の反復処理の実例も紹介しました。
これらの知識を活用して、実際のプログラミングにおいて効率的で可読性の高いコードを書くことに挑戦してみてください。