STLのイテレータ
[第]34~35[課では、]STLコンテナ[と]アルゴリズム[について学びました。]
[さて]、[ここでは]STLの「接着剤」とも言えるiteratorについて詳しく見ていきましょう。
[イテレータ]を理解してこそ、[STLの設計哲学]を[真に理解できる]。
1. イテレータ[概要]
(1) 1.1 イテレータとは?
イテレータはSTLの[中核となる概念]であり、[コンテナ]と[アルゴリズム]を[結びつける]ものです。
[生活]クラス[比]:
- container = [倉庫]
- アルゴリズム = [労働者]
- イテレータ = [作業員が持っている][出荷伝票]([どの棚から商品を取り出すかを作業員に指示する])
(2) 1.2 イテレータの基本操作
[すべての]イテレータ[は以下の操作をサポートしています]:
| [操作] | [説明] | 例 |
|---|---|---|
*it |
[解]参照 | int x = *it; |
it++ |
[次の要素へ進む] | ++it; |
it-- |
[前の要素に戻る] | --it; |
it1 == it2 |
[等しい] | if (it1 == it2) |
it1 != it2 |
[比較不等] | while (it != end()) |
2. iterator[分]クラス
(1) 2.1 [5種類の]イテレータ
STLでは、5種類のイテレータが定義されており、その機能は[弱いものから強いものへと]順に並んでいます:
| 反復処理型 | [機能] | [代表例]コンテナ |
|---|---|---|
| inputiterator | [読み取り専用],[一方向] | istream_iterator |
| 出力イテレータ | [書き込み専用],[一方向] | ostream_iterator |
| [順方向]イテレータ | [読み書き],[一方向] | forward_list |
| [双方向]イテレータ | [読み書き]、[双方向] | list、set、map |
| [ランダムアクセス]イテレータ | [読み書き]、[ランダムアクセス] | vector、deque、array |
(2) 2.2 イテレータ[能力]の比較
←
↓
↓
↓
←
[能力が高ければ高いほど]、[対応する操作も増える]:
| [操作] | 入力 | [順方向] | [双方向] | [ランダムアクセス] |
|---|---|---|---|---|
[解]reference * |
✅ | ✅ | ✅ | ✅ |
[次へ] ++ |
✅ | ✅ | ✅ | ✅ |
[戻る] -- |
❌ | ❌ | ✅ | ✅ |
| [ランダムアクセス] `` | ❌ | ❌ | ❌ | ✅ |
[算術演算] + - |
❌ | ❌ | ❌ | ✅ |
(3) 2.3 の例:[異なる]コンテナ[の]イテレータ
#include iostream
#include vector
#include list
#include forward_list
int main() {
std::vectorint v = {1, 2, 3};
std::listint l = {1, 2, 3};
std::forward_listint fl = {1, 2, 3};
// vector:iterator
auto it_v = v.begin();
std::cout << it_v[2] << std::endl; //
// list:iterator
auto it_l = l.begin();
++it_l; // ...
--it_l; // ...
// it_l[2]; // ❌!list
return 0;
}
3. イテレータ[無効]
(1) 3.1 イテレータ[失効]とは何か?
イテレータ[無効化]とは、コンテナ[に対する操作の後]、イテレータ[が指していた位置が][無効]になることを指します。
[よくある原因]:
- メモリ[再割り当て](
vector[の]push_back) - [要素の削除](
erase)
(2) 3.2 [異なる]コンテナ[の]イテレータ[の無効化ルール]
| コンテナ | [操作] | [無効となる状況] |
|---|---|---|
vector |
push_back |
[無効になる可能性あり]([再割り当て時]) |
vector |
erase |
[削除された要素およびそれ以降の]イテレータ[無効] |
list |
push_back |
[失効なし] |
list |
erase |
[削除された要素のみを含む]イテレータ[無効] |
map/set |
erase |
[削除された要素のみを含む]イテレータ[無効] |
(3) 3.3 例:vectoriterator[無効] (難易度 ⭐⭐)
▶ サンプル 1: STLコンテナ[の使用] (難易度 ⭐)
#include iostream
#include vector
int main() {
std::vectorint v = {1, 2, 3, 4, 5};
auto it = v.begin();
std::cout << "*it = " << *it << std::endl; // output:1
// push_back,iterator
v.push_back(6);
// ❌!it
// std::cout << "*it = " << *it << std::endl; // ...
// ✅:iterator
it = v.begin();
std::cout << "*it = " << *it << std::endl; // output:1
return 0;
}
(4) 3.4 [要素の安全な削除]
エラー[作り方]:
for (auto it = v.begin(); it != v.end(); ++it) {
### (5) ▶ 2:C++(⭐)
if (*it % 2 == 0) {
v.erase(it); // ❌ it!
}
}
[正しい方法]:
for (auto it = v.begin(); it != v.end(); ) {
if (*it % 2 == 0) {
it = v.erase(it); // ✅ erasereturnYesiterator
} else {
++it;
}
}
4. [逆方向]イテレータ
(1) 4.1 [逆]イテレータとは何か?
[逆方向]イテレータは、[コンテナ]の[末尾から先頭に向かって]走査します。
例:[逆順]の出力 (難易度 ⭐)
#include iostream
#include vector
int main() {
std::vectorint v = {1, 2, 3, 4, 5};
// ...
for (auto it = v.rbegin(); it != v.rend(); ++it) {
std::cout << *it << " ";
}
std::cout << std::endl;
return 0;
}
[実行結果]:
5 4 3 2 1
5. [挿入]イテレータ
(1) 5.1 [挿入]イテレータとは何か?
[挿入]イテレータは、[コンテナに]要素を****[挿入する]ために[使用される][出力]イテレータです。
[3種類の挿入]イテレータ:
| イテレータ | [機能] | 例 |
|---|---|---|
back_inserter |
[末尾に挿入] | std::back_inserter(v) |
front_inserter |
[先頭挿入] | std::front_inserter(l) |
inserter |
[指定位置に挿入] | std::inserter(v, v.begin()) |
(2) 5.2 例:[用]back_inserter[コピー] (難易度 ⭐⭐)
#include iostream
#include vector
#include algorithm
#include iterator
int main() {
std::vectorint src = {1, 2, 3, 4, 5};
std::vectorint dst; // ...
// ❌!dst
// std::copy(src.begin(), src.end(), dst.begin());
// ✅!Usingback_inserter
std::copy(src.begin(), src.end(), std::back_inserter(dst));
std::cout << ":";
for (int x : dst) {
std::cout << x << " ";
}
std::cout << std::endl;
return 0;
}
6. ストリームイテレータ
(1) 6.1 入力ストリームイテレータ
[機能]: [inputstreamから]データを[読み込む]。
例:[標準]入力[から読み取り] (難易度 ⭐⭐)
#include iostream
#include vector
#include iterator
int main() {
std::vectorint v;
std::cout << "(Ctrl+Z):" << std::endl;
// ...
std::copy(std::istream_iteratorint(std::cin),
std::istream_iteratorint(),
std::back_inserter(v));
std::cout << ":";
for (int x : v) {
std::cout << x << " ";
}
std::cout << std::endl;
return 0;
}
(2) 6.2 出力ストリームイテレータ
[機能]: [outputstream]に[データを書き込む]。
例:output[to]file (難易度 ⭐⭐)
#include iostream
#include vector
#include iterator
#include fstream
int main() {
std::vectorint v = {1, 2, 3, 4, 5};
// outputoutput
std::copy(v.begin(), v.end(),
std::ostream_iteratorint(std::cout, " "));
std::cout << std::endl;
// output
std::ofstream file("output.txt");
std::copy(v.begin(), v.end(),
std::ostream_iteratorint(file, "\n"));
file.close();
return 0;
}
❓ よくある質問
list [リストは連結リスト]であり、[要素は]メモリ[内で連続していない]ため、[インデックスを使って直接アクセスすることはできない]からです。eraseの[戻り値]を使ってイテレータを[更新]する 3. [できるだけ]アルゴリズムを使用し、イテレータへの[手動操作]を控えるQ:const_iteratorとは何ですか? A:
const_iterator[読み取り専用]のイテレータであり、[要素の値を修正することはできません]。
std::vectorint v = {1, 2, 3};
std::vectorint::const_iterator it = v.cbegin();
// *it = 10; // ❌!modify
📖 まとめ
| [知識ポイント] | [要点] |
|---|---|
| iterator[分]クラス | 入力→[順方向]→[双方向]→[ランダムアクセス] |
| iterator[無効] | vector[無効になる可能性あり],list[無効にならない] |
| [逆方向]イテレータ | rbegin()/rend() |
| [挿入]イテレータ | back_inserter[など] |
| streamiterator | [STLと]IO |
📝 練習問題
-
**初心者(難易度 ⭐):[iterator] を使用して vectorint を [遍歴] し、[すべての要素] を出力する。[begin] と [end] を使用する方法と、[for] ループを使用する方法の [2つの方法] でそれぞれ行う。
-
**中級(難易度 ⭐⭐):[逆順]イテレータ rbegin/rend を使用して [ベクトルを逆順に走査]し、[結果を]出力する。[順方向の走査との違いを観察する]。
-
**上級(難易度 ⭐⭐⭐):[カスタム]イテレータを実装し、[整数の範囲]([例えば] 1 [から] 10)を[ラップ]し、[++] [および] [*] [演算子]を[サポート]する。
- イテレータは、コンテナとアルゴリズムをつなぐ架け橋である
- [5種類の]イテレータ:入力/出力/[順方向]/[双方向]/[ランダムアクセス]
- [範囲] forループ[低レベル用途]イテレータ[実装]
- イテレータ[無効]:[挿入]/[削除後、一部の]イテレータ[利用不可]
- const_iterator [要素への読み取り専用アクセス]
[次のレッスン]:STLの関数オブジェクト(#38)



