---
title: "C++中迭代器的一些理解"
date: 2026-09-06
category: "C++"
tags: ["C++", "STL", "iterator"]
summary: "迭代器到底是什么，end()指向的是哪，为什么erase之后迭代器会失效。"
series:
  name: "C++STL容器及附属"
  order: 0
---

## 引言

在使用vector和string时，我其实早就见过迭代器了，insert和erase的参数都是迭代器，find这类算法的返回值也是迭代器。但迭代器到底是什么，end()指向的是哪，为什么erase之后迭代器会失效，这些一直没有认真想过。

于是我写了一个iterator的使用实验，把获取、遍历、查找、删除串在一起跑了一遍。

在此篇文章中，我将记录下自己对迭代器的理解，以供思考，如有不足之处欢迎指正。

## 正文

### 迭代器是什么

我的理解是，**迭代器是统一访问容器元素的抽象指针**。
它像指针一样支持解引用和自增，但又把容器底层的实现细节（数组、链表还是哈希桶）藏了起来，算法只跟迭代器打交道，不关心容器是什么。

获取迭代器的两个端点是begin()和end()：

```cpp
vector vec{0, 11, 22, 33, 44, 55, 66, 66, 66, 77};
auto begin{std::begin(vec)};//std::begin，也可以用vec.begin()
auto end{vec.end()};
cout << *(end - 1) << endl;//end指向最后一个元素的下一个位置
```

end()并不指向最后一个元素，而是指向**最后一个元素的下一个位置**，所以`*(end - 1)`才是最后一个元素。
这么看来，end()更像是一个“终点哨兵”，遍历条件`p != end`在遍历完所有元素后恰好成立。

### 与算法配合

标准库算法几乎都以迭代器对作为输入：

```cpp
auto it = std::find(begin, end, 3);//查找值，找不到返回end
if (it == end) { cout << "Not found 3" << endl; }
it = std::find(begin, end, 33);
cout << *it << " is in pos" << it - begin << endl;//迭代器相减得到下标

auto f = [](const int &t) { return t == 22; };
it = std::find_if(begin, end, f);//按谓词查找

auto count = std::count(begin, end, 66);//统计出现次数
count = std::count_if(begin, end, [](int t) { return t < 40; });

std::sort(begin, end, std::greater{});//降序排序
std::for_each(std::next(begin), end, [](int t) { cout << t << ' '; });
```

find()找不到时返回end()，这个约定使得“未找到”的判断非常统一，判别方式就是`it == end`。
而`it - begin`能得到下标，`std::next(begin)`能拿到偏移后的迭代器，这些都是迭代器像指针一样支持运算的体现。

### 迭代器失效

这是实验里最重要的一处细节：

```cpp
//必须重赋值，否则迭代器会失效，erase()返回下一个元素
it = vec.erase(it);
```

erase()删除元素后，指向被删元素及之后位置的迭代器都会失效，**erase()的返回值指向被删元素的下一个元素，必须用它重新赋值迭代器**。

> 以vector为例，erase()会让删除点之后的所有迭代器失效，因为后面的元素要整体前移；这与我在vector实验里提到的扩容导致迭代器失效是同一类问题，根源都是元素搬移。

### 补充要点

迭代器配合vector和string的使用方式基本一致，begin()、end()、erase()的语义都是共通的，这也是前面vector实验里insert()与erase()能以迭代器为参数的原因。

另外值得一提的是，c++20的range-based for底层就是靠begin()和end()工作的，像`std::views::reverse(vec)`生成的视图同样可以迭代，本质上还是迭代器在背后支撑。

## 小结

迭代器是连接容器与算法的桥梁，容器负责提供begin()和end()，算法只依赖迭代器对工作，两边的实现互不耦合。用迭代器写代码时最需要记住的两件事：end()指向最后元素的下一个位置，以及任何可能搬移元素的操作之后都要重新获取迭代器。

把迭代器当作“被抽象过的指针”来理解，大部分行为就都可以用指针的直觉去预测了。

本文所用的测试代码收录于[Learncpp](https://github.com/ManJin03/Learncpp)，如有不妥之处欢迎指正。
