イテレータ
『C++』のイテレータはコンテナ(vector・list・map など)の要素を順に走査するためのオブジェクトです。ポインタと同じように * で要素を取得し、++ で次へ進めます。イテレータを使うことでコンテナの内部実装に依存しない汎用的な走査コードを書けます。
構文
// ========================================
// イテレータの基本構文
// ========================================
#include <vector>
#include <iostream>
std::vector<int> v = {10, 20, 30};
// begin() / end() でイテレータを取得します
// begin() は先頭要素を指し、end() は末尾の「次」を指します
std::vector<int>::iterator it = v.begin();
// * 演算子で要素にアクセスします
std::cout << *it << std::endl; // 10
// ++ で次の要素へ進みます
++it;
std::cout << *it << std::endl; // 20
// for ループでコンテナ全体を走査します
for (std::vector<int>::iterator it = v.begin(); it != v.end(); ++it) {
std::cout << *it << std::endl;
}
// auto を使うと型を省略できます(C++11 以降)
for (auto it = v.begin(); it != v.end(); ++it) {
std::cout << *it << std::endl;
}
// const_iterator: 要素を書き換えられない読み取り専用イテレータです
std::vector<int>::const_iterator cit = v.cbegin();
// reverse_iterator: 末尾から先頭に向かって走査します
for (auto rit = v.rbegin(); rit != v.rend(); ++rit) {
std::cout << *rit << std::endl; // 30, 20, 10 の順に出力されます
}
イテレータの種類
| 種類 | 取得方法 | 概要 |
|---|---|---|
| iterator | begin() / end() | 先頭から末尾に向けて走査する基本的なイテレータです。要素の読み書きができます。 |
| const_iterator | cbegin() / cend() | 読み取り専用のイテレータです。*it = 値 のような書き換えはコンパイルエラーになります。 |
| reverse_iterator | rbegin() / rend() | 末尾から先頭に向けて逆順に走査するイテレータです。++ で前の要素へ移動します。 |
| const_reverse_iterator | crbegin() / crend() | 逆順かつ読み取り専用のイテレータです。 |
イテレータカテゴリ
| カテゴリ | 対応コンテナ例 | 概要 |
|---|---|---|
| 入力イテレータ(Input Iterator) | istream_iterator | 一方向への読み取り専用走査ができます。++ と * のみ使用できます。 |
| 出力イテレータ(Output Iterator) | ostream_iterator | 一方向への書き込み専用走査ができます。 |
| 前進イテレータ(Forward Iterator) | forward_list | 一方向の読み書き走査ができます。複数回のパスが可能です。 |
| 双方向イテレータ(Bidirectional Iterator) | list, map, set | ++ に加えて -- による後退も可能です。 |
| ランダムアクセスイテレータ(Random Access Iterator) | vector, deque, array | it + n や it[n] などで任意の位置に直接アクセスできます。最も機能が豊富です。 |
サンプルコード
jujutsu_iterators.cpp
// ========================================
// jujutsu_iterators.cpp
// 呪術廻戦の呪術師データをコンテナに格納し
// 各種イテレータで走査するサンプルです
// ========================================
#include <iostream>
#include <string>
#include <vector>
#include <list>
#include <map>
#include <algorithm>
// ========================================
// 呪術師を表す構造体です
// ========================================
struct Sorcerer {
std::string name; // 術師の名前です
int cursedEnergy; // 呪力の値です
};
// ========================================
// vector + iterator: 基本的な前進走査です
// ========================================
void demonstrateVectorIterator() {
std::vector<Sorcerer> sorcerers = {
{"虎杖悠仁", 100},
{"伏黒恵", 85},
{"釘崎野薔薇", 80},
{"七海建人", 90},
{"五条悟", 999},
};
std::cout << "--- vector: 前進イテレータ ---" << std::endl;
// auto で iterator の型を省略します
for (auto it = sorcerers.begin(); it != sorcerers.end(); ++it) {
// -> 演算子でメンバにアクセスします
std::cout << it->name << " 呪力: " << it->cursedEnergy << std::endl;
}
std::cout << std::endl;
// ========================================
// const_iterator: 読み取り専用で安全に走査します
// ========================================
std::cout << "--- vector: const_iterator(読み取り専用) ---" << std::endl;
for (auto cit = sorcerers.cbegin(); cit != sorcerers.cend(); ++cit) {
// cit->cursedEnergy = 0; // これはコンパイルエラーになります
std::cout << cit->name << " 呪力: " << cit->cursedEnergy << std::endl;
}
std::cout << std::endl;
// ========================================
// reverse_iterator: 末尾から先頭に走査します
// ========================================
std::cout << "--- vector: reverse_iterator(逆順走査) ---" << std::endl;
for (auto rit = sorcerers.rbegin(); rit != sorcerers.rend(); ++rit) {
std::cout << rit->name << " 呪力: " << rit->cursedEnergy << std::endl;
}
std::cout << std::endl;
// ========================================
// ランダムアクセス: vector は添字演算子で任意位置へジャンプできます
// ========================================
std::cout << "--- vector: ランダムアクセスイテレータ ---" << std::endl;
auto it = sorcerers.begin();
it += 2; // 先頭から2つ進みます(釘崎野薔薇)
std::cout << "3番目の術師: " << it->name << std::endl;
// イテレータ同士の差で距離を計算できます
auto first = sorcerers.begin();
auto last = sorcerers.end();
std::cout << "術師の人数: " << (last - first) << std::endl;
std::cout << std::endl;
}
// ========================================
// list + iterator: 双方向イテレータです
// list は ++ と -- が使えますが it + n は使えません
// ========================================
void demonstrateListIterator() {
std::list<std::string> gradeList = {
"虎杖悠仁(1年)",
"伏黒恵(1年)",
"釘崎野薔薇(1年)",
"七海建人(2級→1級)",
"五条悟(特級)",
};
std::cout << "--- list: 双方向イテレータ(前進)---" << std::endl;
for (auto it = gradeList.begin(); it != gradeList.end(); ++it) {
std::cout << *it << std::endl;
}
std::cout << std::endl;
// -- 演算子で後退できます(双方向イテレータの特徴です)
std::cout << "--- list: 末尾から1つ前の要素 ---" << std::endl;
auto it = gradeList.end();
--it; // end() は末尾の次を指すので、-- して末尾要素を指します
std::cout << "末尾: " << *it << std::endl;
--it;
std::cout << "末尾-1: " << *it << std::endl;
std::cout << std::endl;
}
// ========================================
// map + iterator: キーと値のペアを走査します
// map のイテレータは pair<const Key, Value> を返します
// ========================================
void demonstrateMapIterator() {
// 術師名 → 呪力グレード のマップです
std::map<std::string, std::string> gradeMap;
gradeMap["虎杖悠仁"] = "特級相当";
gradeMap["伏黒恵"] = "準1級";
gradeMap["釘崎野薔薇"] = "3級→準1級";
gradeMap["七海建人"] = "1級";
gradeMap["五条悟"] = "特級";
std::cout << "--- map: イテレータでキーと値を走査 ---" << std::endl;
// map のイテレータは first(キー)と second(値)を持ちます
for (auto it = gradeMap.begin(); it != gradeMap.end(); ++it) {
std::cout << it->first << ": " << it->second << std::endl;
}
std::cout << std::endl;
// find() はキーを指すイテレータを返します
// キーが存在しない場合は end() を返します
std::cout << "--- map: find() でイテレータ検索 ---" << std::endl;
auto found = gradeMap.find("五条悟");
if (found != gradeMap.end()) {
std::cout << found->first << " は " << found->second << " です。" << std::endl;
}
auto notFound = gradeMap.find("釈迦");
if (notFound == gradeMap.end()) {
std::cout << "釈迦 はマップに存在しません。" << std::endl;
}
std::cout << std::endl;
}
// ========================================
// イテレータと標準アルゴリズム
// STL のアルゴリズムはイテレータの範囲 [begin, end) で動作します
// ========================================
void demonstrateAlgorithm() {
std::vector<int> powers = {100, 85, 80, 90, 999};
// 各要素は順に 虎杖・伏黒・釘崎・七海・五条 の呪力値です
std::cout << "--- std::find: 値を線形探索します ---" << std::endl;
// 呪力90(七海建人)を検索します
auto it = std::find(powers.begin(), powers.end(), 90);
if (it != powers.end()) {
// イテレータ同士の差でインデックスを求めます
int idx = it - powers.begin();
std::cout << "呪力90 は " << idx << " 番目に見つかりました。" << std::endl;
}
std::cout << "--- std::sort: 呪力値を昇順に並び替えます ---" << std::endl;
// sort() は [begin, end) の範囲を直接書き換えます
std::sort(powers.begin(), powers.end());
for (auto it2 = powers.begin(); it2 != powers.end(); ++it2) {
std::cout << *it2 << " ";
}
std::cout << std::endl << std::endl;
std::cout << "--- 部分範囲への適用: 先頭3要素だけを並び替えます ---" << std::endl;
std::vector<int> powers2 = {100, 85, 80, 90, 999};
// begin() から begin()+3 の範囲だけを昇順ソートします
std::sort(powers2.begin(), powers2.begin() + 3);
for (auto it3 = powers2.begin(); it3 != powers2.end(); ++it3) {
std::cout << *it3 << " ";
}
std::cout << std::endl;
}
int main() {
std::cout << "=============================" << std::endl;
std::cout << " イテレータ サンプル(呪術廻戦)" << std::endl;
std::cout << "=============================" << std::endl << std::endl;
demonstrateVectorIterator();
demonstrateListIterator();
demonstrateMapIterator();
demonstrateAlgorithm();
return 0;
}
# コンパイルします g++ -std=c++11 jujutsu_iterators.cpp -o jujutsu_iterators # 実行します ./jujutsu_iterators ============================= イテレータ サンプル(呪術廻戦) ============================= --- vector: 前進イテレータ --- 虎杖悠仁 呪力: 100 伏黒恵 呪力: 85 釘崎野薔薇 呪力: 80 七海建人 呪力: 90 五条悟 呪力: 999 --- vector: const_iterator(読み取り専用) --- 虎杖悠仁 呪力: 100 伏黒恵 呪力: 85 釘崎野薔薇 呪力: 80 七海建人 呪力: 90 五条悟 呪力: 999 --- vector: reverse_iterator(逆順走査) --- 五条悟 呪力: 999 七海建人 呪力: 90 釘崎野薔薇 呪力: 80 伏黒恵 呪力: 85 虎杖悠仁 呪力: 100 --- vector: ランダムアクセスイテレータ --- 3番目の術師: 釘崎野薔薇 術師の人数: 5 --- list: 双方向イテレータ(前進)--- 虎杖悠仁(1年) 伏黒恵(1年) 釘崎野薔薇(1年) 七海建人(2級→1級) 五条悟(特級) --- list: 末尾から1つ前の要素 --- 末尾: 五条悟(特級) 末尾-1: 七海建人(2級→1級) --- map: イテレータでキーと値を走査 --- 五条悟: 特級 七海建人: 1級 虎杖悠仁: 特級相当 釘崎野薔薇: 3級→準1級 伏黒恵: 準1級 --- map: find() でイテレータ検索 --- 五条悟 は 特級 です。 釈迦 はマップに存在しません。 --- std::find: 値を線形探索します --- 呪力90 は 3 番目に見つかりました。 --- std::sort: 呪力値を昇順に並び替えます --- 80 85 90 100 999 --- 部分範囲への適用: 先頭3要素だけを並び替えます --- 80 85 100 90 999
よくあるミス: end() を逆参照する
end() は末尾要素の「次」を指す番兵であり、要素を指していません。*end() で逆参照すると未定義動作になります。ループの終了条件は it != v.end() で判定します。
// NG: end() を逆参照しています
std::vector<int> powers = {100, 85, 999};
auto it = powers.end();
std::cout << *it << std::endl; // 未定義動作です
OK: ループの条件で it != v.end() を使い、ループ内で逆参照します。
// OK:
for (auto it = powers.begin(); it != powers.end(); ++it) {
std::cout << *it << std::endl;
}
よくあるミス: イテレータの無効化
vector の要素を追加・削除するとイテレータが無効化される場合があります(再確保による無効化)。イテレータを使ってループ中に要素を追加・削除するときは注意が必要です。
// NG: ループ中に push_back するとイテレータが無効化される場合があります
std::vector<int> v = {1, 2, 3};
for (auto it = v.begin(); it != v.end(); ++it) {
v.push_back(*it); // 再確保が起きると it は無効になります
}
OK: 変更が必要な場合はインデックスを使うか、変更後にイテレータを再取得します。
// OK: インデックスで走査します
int size = (int)v.size();
for (int i = 0; i < size; i++) {
v.push_back(v[i]);
}
概要
イテレータは『C++』STL の中心的な概念であり、コンテナとアルゴリズムをつなぐ橋渡しの役割を担います。begin() / end() で取得した範囲 [begin, end) を渡すことで、std::sort・std::find・std::copy などの汎用アルゴリズムをどのコンテナにも適用できます。イテレータには5つのカテゴリがあり、vector や deque はランダムアクセス(it + n が可能)、list や map は双方向(-- が可能)、forward_list は前進のみと、コンテナごとに能力が異なります。コンテナの要素を変更したくない場合は cbegin() / cend() による const_iterator を使うと変更できない状態が保証されます。また rbegin() / rend() を使えば逆順走査を手軽に実現できます。map のイテレータは std::pair を返すため、it->first(キー)と it->second(値)でアクセスします。イテレータ同士の差(it2 - it1)でインデックス距離を求められるのはランダムアクセスイテレータだけである点に注意が必要です。
記事の間違いや著作権の侵害等ございましたらお手数ですがこちらまでご連絡頂ければ幸いです。