在 C++ 开发中,迭代器是容器访问的核心工具。虽然最常见的用法是使用标准容器(如 std::vector、std::list 等)自带的迭代器,但真正的强大之处在于我们可以自行定义迭代器来满足特定需求。下面从三个层面——指针、标准库提供的 std::iterator 基础类以及自定义迭代器——对比它们的实现细节、适用场景以及如何组合使用,帮助你在项目中更灵活地操纵数据。
1. 指针作为迭代器
1.1 简单易用
在数组和 C 风格的容器中,原始指针就天然地充当了迭代器。它们满足了所有 C++ 迭代器概念所要求的成员操作:
*it 取值
++it 前置递增
it++ 后置递增
it == other 比较
int arr[5] = {1,2,3,4,5};
for (int* it = arr; it != arr + 5; ++it) {
std::cout << *it << ' ';
}
1.2 局限性
- 仅支持顺序访问,无法轻易实现随机访问的其他约束。
- 与容器生命周期绑定,容器变动时可能导致悬空指针。
- 缺乏类型安全,容易出现越界或空指针访问。
2. std::iterator(或更现代的 std::iterator_traits)
2.1 继承自 std::iterator
在 C++17 之前,创建自定义迭代器最常用的做法是继承 std::iterator,并提供所需的 value_type、pointer、reference、difference_type、iterator_category 等 typedefs。
template <typename T>
class SimpleIter : public std::iterator<std::random_access_iterator_tag, T> {
public:
SimpleIter(T* ptr) : ptr_(ptr) {}
T& operator*() const { return *ptr_; }
SimpleIter& operator++() { ++ptr_; return *this; }
// 其余操作略
private:
T* ptr_;
};
2.2 现代实践
C++20 推出了更简洁的方式:直接使用 std::iterator_traits,不再需要继承 std::iterator。只需满足以下成员即可:
template <typename T>
class SimpleIter {
public:
using value_type = T;
using difference_type = std::ptrdiff_t;
using pointer = T*;
using reference = T&;
using iterator_category = std::random_access_iterator_tag;
// 操作符实现
};
这种方式更符合现代 C++ 的“无继承”理念,减少了冗余代码。
3. 自定义迭代器:实战案例
3.1 场景
假设我们实现一个 “可逆链表”,其中每个节点除了指向下一个节点外,还指向上一个节点。我们希望迭代器既能正向遍历也能反向遍历。
3.2 设计思路
- 迭代器类型:双向迭代器(
std::bidirectional_iterator_tag)。
- 内部维护
Node* current_。
- 支持
++ 前置递增、-- 前置递减。
- 需要实现比较、解引用、加减运算符(若实现随机访问可选)。
3.3 代码实现
template <typename T>
class ReversibleLinkedList {
struct Node {
T data;
Node* next;
Node* prev;
Node(const T& v) : data(v), next(nullptr), prev(nullptr) {}
};
Node* head_;
Node* tail_;
public:
ReversibleLinkedList() : head_(nullptr), tail_(nullptr) {}
// 省略 push_back/pop_front 等操作
class Iterator {
public:
using iterator_category = std::bidirectional_iterator_tag;
using value_type = T;
using difference_type = std::ptrdiff_t;
using pointer = T*;
using reference = T&;
Iterator(Node* ptr) : node_(ptr) {}
reference operator*() const { return node_->data; }
pointer operator->() const { return &(node_->data); }
// 前置递增
Iterator& operator++() { node_ = node_->next; return *this; }
// 后置递增
Iterator operator++(int) { Iterator tmp = *this; ++(*this); return tmp; }
// 前置递减
Iterator& operator--() { node_ = node_->prev; return *this; }
// 后置递减
Iterator operator--(int) { Iterator tmp = *this; --(*this); return tmp; }
bool operator==(const Iterator& other) const { return node_ == other.node_; }
bool operator!=(const Iterator& other) const { return node_ != other.node_; }
private:
Node* node_;
};
Iterator begin() { return Iterator(head_); }
Iterator end() { return Iterator(nullptr); }
Iterator rbegin() { return Iterator(tail_); }
Iterator rend() { return Iterator(nullptr); }
};
3.4 使用示例
ReversibleLinkedList <int> list;
list.push_back(10);
list.push_back(20);
list.push_back(30);
for (auto it = list.begin(); it != list.end(); ++it) {
std::cout << *it << ' '; // 10 20 30
}
std::cout << '\n';
for (auto it = list.rbegin(); it != list.rend(); --it) {
std::cout << *it << ' '; // 30 20 10
}
4. 何时使用哪种迭代器?
| 需求 |
推荐迭代器类型 |
说明 |
| 简单遍历固定大小数组 |
指针 |
代码简洁,性能最高 |
| 需要类型安全、跨容器一致接口 |
std::iterator/std::iterator_traits |
与 STL 容器迭代器兼容 |
| 自定义容器或非标准数据结构 |
自定义迭代器 |
可实现任意访问策略、延迟加载等功能 |
5. 小结
- 指针是最轻量的迭代器,适用于原始数组和已知生命周期的数据。
std::iterator/std::iterator_traits 为自定义迭代器提供标准接口,兼容 STL 生态。
- 自定义迭代器让你可以为任何数据结构提供遍历能力,提升代码复用性与可维护性。
掌握这三种迭代器的使用方式,你就能在 C++ 项目中无论是快速原型还是大规模系统,都能灵活选择最合适的遍历工具。