C++之迭代器 (Iterator) 全面教程

159 阅读7分钟

迭代器是 C++ 中用于 遍历容器(如数组、链表、哈希表等)  的工具,它像 智能指针 一样,可以逐个访问容器中的元素,而不用关心容器的内部实现。以下是迭代器的全面教程:


1. 为什么需要迭代器?

假设你要遍历一个数组和一个链表,传统方式需要不同的代码:

// 遍历数组
int arr[5] = {1, 2, 3, 4, 5};
for (int i = 0; i < 5; i++) { /* 用下标访问 arr[i] */ }

// 遍历链表
struct Node { int data; Node* next; };
Node* head = /* 链表头 */;
for (Node* p = head; p != nullptr; p = p->next) { /* 用指针访问 p->data */ }

问题:两种方式完全不同,代码难以复用。

迭代器 提供 统一的访问方式,无论容器是数组、链表还是哈希表,都可以用相同的方法遍历:

// 统一用迭代器遍历
auto it = container.begin();  // 获取开始位置
while (it != container.end()) {  // 判断是否到末尾
    std::cout << *it;  // 访问当前元素
    ++it;  // 移动到下一个元素
}

2. 迭代器的基本使用

(1) 获取迭代器

  • begin() → 指向 第一个元素 的迭代器
  • end() → 指向 最后一个元素的下一个位置(类似“结束标记”)
#include <vector>
#include <iostream>

int main() {
    std::vector<int> vec = {1, 2, 3, 4, 5};

    // 获取迭代器
    auto it = vec.begin();  // 指向 1
    auto end = vec.end();   // 指向 5 的后面(不是 5!)

    // 遍历
    while (it != end) {
        std::cout << *it << " ";  // 解引用,访问当前元素
        ++it;  // 移动到下一个元素
    }
    return 0;
}

输出1 2 3 4 5

(2) 范围-based for 循环(推荐)

C++11 后,可以用更简单的语法遍历:

for (int val : vec) {  // 自动用迭代器遍历
    std::cout << val << " ";
}

等价于

for (auto it = vec.begin(); it != vec.end(); ++it) {
    int val = *it;
    std::cout << val << " ";
}

3. 迭代器的核心操作

操作说明示例
*it解引用,访问当前元素*it = 10;(修改元素)
++it移动到下一个元素it++;++it;
it != end判断是否遍历结束while (it != vec.end())
it1 == it2比较两个迭代器if (it1 == it2)

示例:修改容器元素

std::vector<int> vec = {1, 2, 3};
for (auto it = vec.begin(); it != vec.end(); ++it) {
    *it *= 2;  // 每个元素乘以 2
}
// vec 变成 {2, 4, 6}

4. 迭代器与不同容器

(1) 数组 (std::vector, std::array)

std::vector<int> vec = {1, 2, 3};
for (auto it = vec.begin(); it != vec.end(); ++it) {
    std::cout << *it << " ";
}

(2) 链表 (std::list)

std::list<int> lst = {1, 2, 3};
for (auto it = lst.begin(); it != lst.end(); ++it) {
    std::cout << *it << " ";
}

(3) 哈希表 (std::unordered_map)

std::unordered_map<std::string, int> ageMap = {{"Alice", 25}, {"Bob", 30}};
for (auto it = ageMap.begin(); it != ageMap.end(); ++it) {
    std::cout << it->first << " -> " << it->second << std::endl;
    // it->first 是键,it->second 是值
}

(4) 集合 (std::set)

std::set<int> s = {3, 1, 2};
for (auto it = s.begin(); it != s.end(); ++it) {
    std::cout << *it << " ";  // 输出:1 2 3(自动排序)
}

5. 注意事项

(1) end() 不能解引用

auto it = vec.end();
// *it;  // 错误!崩溃!

end() 只是 结束标记,不代表任何实际元素。

(2) 迭代器可能失效

  • 如果对容器 插入/删除元素,某些迭代器可能失效(不能再使用)。
  • 安全做法:插入/删除后,重新获取迭代器。

(3) 不同容器的迭代器功能不同

容器支持的迭代器操作
vectorarray++--+n-n[]
listmapset++--(不支持随机访问)
unordered_map++(不支持 -- 和随机访问)

6. 总结

  • 迭代器 是遍历容器的 通用工具,让代码更简洁、更灵活。
  • 核心操作*it(访问)、++it(移动)、it != end(判断结束)。
  • 推荐 使用范围-based for 循环(for (auto x : container))。
  • 注意 end() 不能解引用,迭代器可能失效。

一句话记忆:迭代器就像 指针 + 规则,让你用统一的方式遍历各种容器! 🚀


📌 1. 迭代器的核心概念

什么是迭代器?

  • 迭代器是 类指针对象,可以遍历容器中的元素
  • 支持 *it(解引用)、++it(前进)、==/!=(比较)等操作
  • 不依赖容器类型:同一算法可以作用于不同容器(如 vectorlist

类比:

  • 游标(如 SQL 游标)、书签指针 的结合体

📌 2. 迭代器类别(5 种)

C++ 标准定义了 5 种迭代器类别,按功能从弱到强排列:

类别支持的操作典型容器示例
输入迭代器只读,单向(++istream_iterator读取文件
输出迭代器只写,单向(++ostream_iterator写入文件
前向迭代器读写,单向(++forward_listunordered_map哈希表
双向迭代器读写,双向(++/--listmapset双向链表
随机访问迭代器读写,随机(+n/-n/[]vectordequearray数组

📊 能力对比

操作输入输出前向双向随机访问
*it✅ 读✅ 写✅ 读/写✅ 读/写✅ 读/写
++it
--it
it + n
it[n]
it1 < it2

📌 3. 基本语法和操作

1. 获取迭代器

#include <vector>
std::vector<int> vec = {1, 2, 3, 4, 5};

// begin() -> 第一个元素
auto itBegin = vec.begin();

// end() -> 末尾标记(不是最后一个元素!)
auto itEnd = vec.end();

2. 迭代器操作

// 解引用(访问元素)
int value = *itBegin;  // value = 1

// 前进到下一个元素
++itBegin;  // 或 itBegin++
*itBegin = 10;  // 修改元素:vec[1] = 10

// 比较
if (itBegin != itEnd) { /* ... */ }

// 随机访问(仅随机访问迭代器)
auto it3 = vec.begin() + 3;  // 指向第 4 个元素
int val = it3[2];           // 等价于 *(it3 + 2),val = 5

3. 遍历容器

// 传统 for 循环
for (auto it = vec.begin(); it != vec.end(); ++it) {
    std::cout << *it << " ";
}

// 范围-based for 循环(C++11+,推荐)
for (const auto& val : vec) {
    std::cout << val << " ";
}

📌 4. 迭代器失效(Critical!)

什么情况下迭代器失效?

容器操作迭代器影响
vector插入/删除所有迭代器可能失效(内存重新分配)
deque首尾插入首尾迭代器失效
list/forward_list插入/删除仅被删除的迭代器失效
map/set插入/删除仅被删除的迭代器失效
unordered_map插入所有迭代器可能失效(重新哈希)

安全实践

// ❌ 危险:删除后继续使用迭代器
auto it = vec.begin();
vec.erase(it);
// *it = 100;  // 未定义行为!

// ✅ 安全:erase 返回下一个有效迭代器
auto it = vec.begin();
it = vec.erase(it);  // 返回下一个元素
*it = 100;           // 安全

📌 5. 迭代器适配器(Iterator Adapters)

1. 反向迭代器(Reverse Iterator)

#include <vector>
std::vector<int> vec = {1, 2, 3, 4, 5};

// rbegin() -> 最后一个元素,rend() -> 第一个元素的前一个位置
for (auto it = vec.rbegin(); it != vec.rend(); ++it) {
    std::cout << *it << " ";  // 输出:5 4 3 2 1
}

2. 插入迭代器(Insert Iterator)

#include <iterator>
#include <vector>
std::vector<int> vec = {1, 2, 3};
std::vector<int> vec2;

// back_inserter:插入到末尾
std::copy(vec.begin(), vec.end(), std::back_inserter(vec2));

// front_inserter:插入到开头(仅支持 list/deque)
std::list<int> lst;
std::copy(vec.begin(), vec.end(), std::front_inserter(lst));

// inserter:插入到指定位置
auto it = vec2.begin() + 1;
std::copy(vec.begin(), vec.end(), std::inserter(vec2, it));

3. 流迭代器(Stream Iterator)

#include <iterator>
#include <iostream>
#include <vector>

// 从 cin 读取整数
std::istream_iterator<int> inIt(std::cin), endIt;
std::vector<int> vec(inIt, endIt);

// 输出到 cout
std::ostream_iterator<int> outIt(std::cout, " ");
std::copy(vec.begin(), vec.end(), outIt);

📌 6. 自定义迭代器

示例:实现一个简单的动态数组迭代器

template <typename T>
class SimpleVector {
private:
    T* data;
    size_t size;

public:
    // 迭代器类
    class Iterator {
    private:
        T* ptr;
    public:
        Iterator(T* p) : ptr(p) {}

        // 必须实现的操作
        T& operator*() { return *ptr; }
        Iterator& operator++() { ++ptr; return *this; }
        bool operator!=(const Iterator& other) const { return ptr != other.ptr; }
    };

    // 容器接口
    Iterator begin() { return Iterator(data); }
    Iterator end() { return Iterator(data + size); }

    // 构造函数等...
    SimpleVector(size_t s) : size(s) { data = new T[s]; }
    ~SimpleVector() { delete[] data; }
};

// 使用
SimpleVector<int> vec(5);
for (auto& val : vec) {
    val = 42;  // 通过迭代器访问
}

📌 7. STL 算法与迭代器

1. 查找

#include <algorithm>
auto it = std::find(vec.begin(), vec.end(), 3);
if (it != vec.end()) {
    std::cout << "Found at position: " << (it - vec.begin()) << std::endl;
}

2. 排序

std::sort(vec.begin(), vec.end());  // 仅随机访问迭代器

3. 变换

std::transform(vec.begin(), vec.end(), vec.begin(), [](int x) { return x * 2; });

4. 删除

auto newEnd = std::remove(vec.begin(), vec.end(), 3);
vec.erase(newEnd, vec.end());

📌 8. 最佳实践

✅ 推荐

  1. 优先使用范围-based for 循环(C++11+):
    for (const auto& val : container) { /* ... */ }
    
  2. 避免保存长期迭代器(可能失效)
  3. 使用 auto 简化迭代器声明
    auto it = container.begin();  // 比 std::vector<int>::iterator 更简洁
    
  4. const 正确性
    const std::vector<int>& vec = getConstVec();
    auto it = vec.cbegin();  // 显式 const 迭代器
    

🚫 避免

  1. 解引用 end() 迭代器
    auto it = vec.end();
    // *it;  // 崩溃!
    
  2. 比较不同容器的迭代器
    auto it1 = vec1.begin();
    auto it2 = vec2.begin();
    // if (it1 == it2) {}  // 未定义行为!
    

📚 总结

主题关键点
核心作用统一访问容器,连接算法
5 种类别输入、输出、前向、双向、随机访问
失效问题插入/删除后需重新获取
适配器反向、插入、流迭代器
最佳实践范围-for、auto、const 正确性

迭代器是 C++ 泛型编程 的基石,理解它才能高效使用 STL! 🚀