1. 導入:なぜイテレータの無効化を学ぶ必要があるのか
C++でstd::vectorなどのコンテナを扱う際、イテレータを使って要素を操作することは非常に一般的です。しかし、ループ中に要素を追加したり削除したりすると、突然プログラムが強制終了したり、予期せぬ値が表示されることがあります。これは「イテレータの無効化」という現象が原因です。この仕組みを理解していないと、バグの温床となるため、C++エンジニアとして必ず押さえておくべき重要な知識です。
2. 基礎知識:イテレータとは何か?
イテレータとは、コンテナ内の要素を指し示す「ポインタのようなもの」です。例えばstd::vectorは、メモリ上に要素を隙間なく並べて保持しています。
もし、vectorの容量がいっぱいになった状態で新しい要素を追加すると、vectorはより大きなメモリ領域を確保し直し、既存の要素をすべて新しい場所にコピー(移動)します。このとき、古いメモリ領域を指していたイテレータは、存在しない場所を指すことになり「無効化」されてしまいます。これがクラッシュの主な原因です。
3. 実装/解決策:無効化を避ける方法
イテレータの無効化を防ぐための鉄則は「要素の追加・削除操作を行った直後に、イテレータを更新する」ことです。
特にeraseメソッドを使用する場合、戻り値として「削除された要素の次の要素を指す有効なイテレータ」が返ってきます。これを受け取ることで、ループを安全に継続できます。
4. サンプルプログラム
以下のコードは、vectorから特定の条件(偶数)を安全に削除する例です。
include <iostream>
include <vector>
int main() {
std::vector<int> v = {1, 2, 3, 4, 5};
// イテレータを取得
auto it = v.begin();
while (it != v.end()) {
if (it % 2 == 0) {
// 削除するとitは無効になるが、
// eraseの戻り値で「次の有効なイテレータ」を受け取ることで回避
it = v.erase(it);
} else {
// 削除しない場合は通常通り進める
++it;
}
}
// 結果の表示
for (int n : v) {
std::cout << n << " "; // 1 3 5 と表示される
}
return 0;
}
5. 応用・注意点
std::vectorだけでなく、std::dequeやstd::listなどのコンテナでも同様の注意が必要です。特にstd::vectorへのpush_backによる再確保は、コード上では目立たないため見落としがちです。
また、std::vector::reserve()を事前に使って、あらかじめ十分なメモリを確保しておくことで、要素追加による再確保(=イテレータの無効化)を抑制できる場合もあります。
「イテレータを使う操作の最中に、コンテナの構造を変える操作はしていないか?」と常に意識することが、堅牢なコードを書くための第一歩です。

コメント