迭代器像指针:* 取出当前元素,++ 前进一格。vector 的 begin() 指向第一个元素,end() 指向最后一个的后面,解引用 end() 非法。范围 for 在底层就是 for (it = begin; it != end; ++it),见范围 for。STL 算法也吃这一对迭代器,所以 std::sort(v.begin(), v.end()) 不必知道 v 的具体类型。本节先写出可运行的遍历,再说明类别、插入迭代器与失效。

begin 与 end

C++ 迭代器 begin 与 end运行
#include <iostream>
#include <vector>

int main() {
    std::vector<int> v = {1, 2, 3};
    for (auto it = v.begin(); it != v.end(); ++it) {
        std::cout << *it << " ";  // *it 是当前元素
    }
    std::cout << std::endl;
    return 0;
}

输出:

1 2 3 

it 支持 ++、*、==。cbegin() / cend() 得到只读迭代器,不能通过 *it 改元素,适合只读遍历。

类别(能力)

类别能力示例容器
随机访问it + n、it[n]、< 比较vector、deque
双向++、--list、map、set
单向仅前进forward_list
输入/输出只读或只写一次流迭代器

std::sort 要求随机访问迭代器;对 list 使用成员 sort。对数组和连续的 vector,指针也满足随机访问,可写 std::sort(arr, arr + n),见指针。

插入迭代器

算法向空 vector 写结果时,不能把 dst.begin() 当作输出位置(里面还没有格子)。std::back_inserter(dst) 每次赋值都会调用 push_back:

C++ back_inserter运行
#include <algorithm>
#include <iostream>
#include <iterator>
#include <vector>

int main() {
    std::vector<int> src{1, 2, 3};
    std::vector<int> dst;
    std::copy(src.begin(), src.end(), std::back_inserter(dst));
    for (int x : dst) {
        std::cout << x << " ";
    }
    std::cout << "\n";
    return 0;
}

输出:

1 2 3 

失效(vector 要点)

vector 扩容会换一块新内存,原先保存的迭代器、指针、引用可能全部失效。在循环里 push_back 时,不要继续使用循环开始时拿到的迭代器,改用下标或每次重新 begin。按值删除元素常用先 remove 再 erase:

v.erase(std::remove(v.begin(), v.end(), value), v.end());

C++20 可用 std::erase / std::erase_if 写得更短。map 删当前元素时用 it = m.erase(it) 取得下一个仍然有效的迭代器。

本节说明了迭代器与算法、容器的关系。下一章从异常处理开始,讨论 try、throw 与 catch。