1. 迭代器模式:数据遍历的通用语言
想象你走进一家图书馆,书架上摆满了各种类型的书籍——精装本、平装本、电子书阅读器,甚至还有几卷古老的羊皮卷轴。作为读者,你只关心如何一本接一本地浏览内容,而不是被不同书籍的物理形态所困扰。这正是迭代器模式(Iterator Pattern)在软件开发中扮演的角色——它让程序员能够用统一的方式遍历各种数据结构,无需关心底层是数组、链表还是更复杂的容器。
在C++的世界里,迭代器早已超越了设计模式的范畴,成为了STL(Standard Template Library)的核心支柱。从std::vector到std::map,从std::list到自定义容器,迭代器作为连接算法与容器的桥梁,实现了"一次编写,到处遍历"的理想。但迭代器模式的价值远不止于此——它还是实现惰性求值、异步数据流处理等高级编程技术的基石。
2. 迭代器模式的四重奏:角色解析
2.1 抽象迭代器(Iterator)
定义访问和遍历元素的接口,通常包含begin(), end(), ++, *等操作。在C++中,这表现为迭代器类型的接口约定,如输入迭代器、前向迭代器、双向迭代器和随机访问迭代器等概念。
cpp复制template<typename T>
class Iterator {
public:
virtual ~Iterator() = default;
virtual T& operator*() const = 0;
virtual Iterator& operator++() = 0;
virtual bool operator!=(const Iterator& other) const = 0;
};
2.2 具体迭代器(ConcreteIterator)
实现抽象迭代器接口,负责管理当前遍历位置。例如std::vector<int>::iterator就是典型的具体迭代器,内部维护着指向vector元素的指针。
2.3 聚合接口(Aggregate)
定义创建迭代器对象的接口。在C++中,容器类的begin()和end()方法就扮演着这个角色:
cpp复制template<typename T>
class Container {
public:
virtual Iterator<T>* createIterator() const = 0;
// ...其他容器接口
};
2.4 具体聚合(ConcreteAggregate)
实现创建迭代器的接口,返回与自身结构对应的具体迭代器实例。比如std::list返回的是双向迭代器,而std::unordered_map返回的是前向迭代器。
3. C++中的迭代器实现范式
3.1 基于类的传统实现
这是最接近GoF原始设计模式的实现方式,适合需要显式控制迭代逻辑的场景:
cpp复制class Book {
std::string title_;
public:
explicit Book(const std::string& title) : title_(title) {}
std::string getTitle() const { return title_; }
};
class Library {
std::vector<Book> books_;
public:
void addBook(const Book& book) { books_.push_back(book); }
class Iterator {
std::vector<Book>::iterator current_;
public:
explicit Iterator(std::vector<Book>::iterator it) : current_(it) {}
Book& operator*() { return *current_; }
Iterator& operator++() { ++current_; return *this; }
bool operator!=(const Iterator& other) { return current_ != other.current_; }
};
Iterator begin() { return Iterator(books_.begin()); }
Iterator end() { return Iterator(books_.end()); }
};
使用示例:
cpp复制Library lib;
lib.addBook(Book("Design Patterns"));
lib.addBook(Book("Effective C++"));
for (auto it = lib.begin(); it != lib.end(); ++it) {
std::cout << it->getTitle() << std::endl;
}
3.2 基于STL风格的迭代器
现代C++更倾向于遵循STL约定,通过类型别名和标准接口使自定义容器与算法无缝协作:
cpp复制template<typename T>
class CircularBuffer {
std::vector<T> buffer_;
size_t head_ = 0;
public:
class iterator {
CircularBuffer* parent_;
size_t pos_;
bool fullCycle_ = false;
public:
// 迭代器类型标签,供STL算法识别
using iterator_category = std::forward_iterator_tag;
using value_type = T;
using difference_type = std::ptrdiff_t;
using pointer = T*;
using reference = T&;
iterator(CircularBuffer* p, size_t pos) : parent_(p), pos_(pos) {}
reference operator*() { return parent_->buffer_[pos_]; }
iterator& operator++() {
pos_ = (pos_ + 1) % parent_->buffer_.size();
if (pos_ == parent_->head_) fullCycle_ = true;
return *this;
}
bool operator!=(const iterator& other) const {
return pos_ != other.pos_ || fullCycle_ != other.fullCycle_;
}
};
CircularBuffer(size_t size) : buffer_(size) {}
iterator begin() { return iterator(this, head_); }
iterator end() { return iterator(this, head_); } // 循环回到起点表示结束
};
3.3 基于C++20的范围for支持
对于简单场景,只需实现begin()和end()方法即可支持范围for循环:
cpp复制class PrimeNumbers {
public:
class Iterator {
int current_ = 2;
bool isPrime(int n) const { /* 素数检测实现 */ }
public:
Iterator& operator++() {
while (!isPrime(++current_));
return *this;
}
int operator*() const { return current_; }
bool operator!=(const Iterator&) const { return true; } // 无限序列
};
Iterator begin() const { return Iterator(); }
Iterator end() const { return Iterator(); }
};
使用示例:
cpp复制PrimeNumbers primes;
int count = 0;
for (int p : primes) {
if (p > 100) break;
std::cout << p << " ";
if (++count == 10) break; // 防止无限循环
}
4. 迭代器分类与性能特征
C++标准定义了五种迭代器类别,每种都有不同的能力保证:
| 迭代器类别 | 支持操作 | 典型代表 |
|---|---|---|
| 输入迭代器 | 只读,单遍扫描 | std::istream_iterator |
| 输出迭代器 | 只写,单遍扫描 | std::ostream_iterator |
| 前向迭代器 | 多遍扫描 | std::forward_list的迭代器 |
| 双向迭代器 | 可递减 | std::list的迭代器 |
| 随机访问迭代器 | 支持算术运算和下标 | std::vector的迭代器 |
关键洞察:迭代器类别形成了层次结构,随机访问迭代器满足所有更低类别的要求。算法会根据迭代器能力选择最优实现,比如
std::distance对随机访问迭代器是O(1)操作,而对输入迭代器则是O(n)。
5. 现代C++中的迭代器进阶技巧
5.1 哨兵迭代器(Sentinel)
C++20引入了哨兵迭代器概念,允许用特殊类型标记序列结束,而非必须使用相同类型的迭代器:
cpp复制struct NullTerminated {};
class CStringIterator {
const char* ptr_;
public:
CStringIterator(const char* p) : ptr_(p) {}
char operator*() const { return *ptr_; }
CStringIterator& operator++() { ++ptr_; return *this; }
bool operator!=(NullTerminated) const { return *ptr_ != '\0'; }
};
class CStringRange {
const char* str_;
public:
CStringRange(const char* s) : str_(s) {}
CStringIterator begin() const { return CStringIterator(str_); }
NullTerminated end() const { return {}; }
};
使用示例:
cpp复制for (char c : CStringRange("Hello")) {
std::cout << c;
}
5.2 惰性求值迭代器
通过迭代器实现惰性计算,可以高效处理大型或无限序列:
cpp复制template<typename Func>
class Generator {
Func func_;
mutable std::optional<decltype(func_())> current_;
public:
class iterator {
const Generator* parent_;
public:
iterator(const Generator* p) : parent_(p) {}
auto operator*() { return *parent_->current_; }
iterator& operator++() {
parent_->current_ = parent_->func_();
return *this;
}
bool operator!=(const iterator& other) const {
return parent_->current_.has_value() != other.parent_->current_.has_value();
}
};
Generator(Func f) : func_(f), current_(func_()) {}
iterator begin() const { return iterator(this); }
iterator end() const { return iterator(nullptr); }
};
auto fibonacci() {
int a = 0, b = 1;
return Generator([a, b]() mutable {
int next = a;
a = b;
b += next;
return next;
});
}
5.3 并行遍历迭代器
C++17引入的执行策略可以与迭代器结合实现并行算法:
cpp复制std::vector<int> data(1'000'000);
std::iota(data.begin(), data.end(), 0);
// 并行变换
std::transform(std::execution::par,
data.begin(), data.end(),
data.begin(),
[](int x) { return x * x; });
6. 迭代器模式在真实项目中的应用案例
6.1 树形结构的多种遍历方式
实现支持深度优先(DFS)和广度优先(BFS)遍历的树迭代器:
cpp复制class TreeNode {
int value_;
std::vector<std::unique_ptr<TreeNode>> children_;
public:
template<typename Iter>
class TreeIterator {
std::stack<TreeNode*> stack_;
Iter iterPolicy_;
public:
TreeIterator(TreeNode* root, Iter policy) : iterPolicy_(policy) {
if (root) iterPolicy_.push(root, stack_);
}
TreeNode& operator*() { return *stack_.top(); }
TreeIterator& operator++() {
TreeNode* current = stack_.top();
stack_.pop();
iterPolicy_.pushChildren(current, stack_);
return *this;
}
bool operator!=(const TreeIterator& other) const {
return !stack_.empty() != !other.stack_.empty();
}
};
struct DFS {
void push(TreeNode* node, std::stack<TreeNode*>& s) { s.push(node); }
void pushChildren(TreeNode* node, std::stack<TreeNode*>& s) {
for (auto it = node->children_.rbegin(); it != node->children_.rend(); ++it) {
s.push(it->get());
}
}
};
struct BFS {
void push(TreeNode* node, std::queue<TreeNode*>& q) { q.push(node); }
void pushChildren(TreeNode* node, std::queue<TreeNode*>& q) {
for (auto& child : node->children_) {
q.push(child.get());
}
}
};
// 使用示例:
void printTree() {
std::cout << "DFS traversal:\n";
for (auto it = TreeIterator<DFS>(this, DFS{}); it != TreeIterator<DFS>(nullptr, DFS{}); ++it) {
std::cout << it->value_ << " ";
}
std::cout << "\nBFS traversal:\n";
for (auto it = TreeIterator<BFS>(this, BFS{}); it != TreeIterator<BFS>(nullptr, BFS{}); ++it) {
std::cout << it->value_ << " ";
}
}
};
6.2 数据库查询结果的流式处理
模拟数据库查询结果的迭代器接口,实现内存高效处理:
cpp复制class DatabaseResult {
struct Cursor; // 前向声明
class Iterator {
std::shared_ptr<Cursor> cursor_;
bool fetchNext() { /* 从游标获取下一条记录 */ }
public:
using value_type = std::map<std::string, std::string>;
Iterator(std::shared_ptr<Cursor> c, bool end = false) : cursor_(c) {
if (!end && !fetchNext()) cursor_ = nullptr;
}
value_type& operator*() { return cursor_->currentRow; }
Iterator& operator++() {
if (!fetchNext()) cursor_ = nullptr;
return *this;
}
bool operator!=(const Iterator& other) const {
return cursor_ != other.cursor_;
}
};
public:
Iterator begin() { return Iterator(createCursor()); }
Iterator end() { return Iterator(nullptr, true); }
};
6.3 图形处理管线中的像素迭代
实现支持多种像素遍历策略的图像处理器:
cpp复制class Image {
std::vector<uint8_t> pixels_;
int width_, height_;
template<typename Strategy>
class PixelIterator {
Image* image_;
int x_, y_;
Strategy strategy_;
public:
PixelIterator(Image* img, int x, int y)
: image_(img), x_(x), y_(y) {}
uint8_t& operator*() { return image_->pixels_[y_ * image_->width_ + x_]; }
PixelIterator& operator++() {
std::tie(x_, y_) = strategy_.next(x_, y_, image_->width_, image_->height_);
return *this;
}
bool operator!=(const PixelIterator& other) const {
return x_ != other.x_ || y_ != other.y_;
}
};
public:
struct RowMajor {
std::pair<int, int> next(int x, int y, int w, int h) {
if (++x >= w) { x = 0; ++y; }
return {x, y};
}
};
struct ColumnMajor {
std::pair<int, int> next(int x, int y, int w, int h) {
if (++y >= h) { y = 0; ++x; }
return {x, y};
}
};
struct Diagonal {
std::pair<int, int> next(int x, int y, int w, int h) {
// 对角线遍历实现
return { (x + 1) % w, (y + 1) % h };
}
};
template<typename Strategy = RowMajor>
PixelIterator<Strategy> begin() { return PixelIterator<Strategy>(this, 0, 0); }
template<typename Strategy = RowMajor>
PixelIterator<Strategy> end() { return PixelIterator<Strategy>(this, 0, height_); }
};
7. 迭代器模式的最佳实践与陷阱规避
7.1 迭代器失效问题
容器修改可能导致迭代器失效,这是C++中最常见的迭代器相关bug:
cpp复制std::vector<int> vec{1, 2, 3};
auto it = vec.begin();
vec.push_back(4); // 可能导致迭代器失效
// *it; // 未定义行为!
解决方案:
- 对
vector/string:插入/删除操作后所有迭代器都可能失效 - 对
deque:中间插入/删除使所有迭代器失效,首尾操作只使部分失效 - 对
list/forward_list/关联容器:只有被删除元素的迭代器失效
7.2 常量正确性
提供const_iterator与iterator的正确重载:
cpp复制class MyContainer {
std::vector<int> data_;
public:
using iterator = std::vector<int>::iterator;
using const_iterator = std::vector<int>::const_iterator;
iterator begin() { return data_.begin(); }
iterator end() { return data_.end(); }
const_iterator begin() const { return data_.begin(); }
const_iterator end() const { return data_.end(); }
const_iterator cbegin() const { return data_.cbegin(); }
const_iterator cend() const { return data_.cend(); }
};
7.3 性能优化技巧
- 迭代器适配器:使用
std::make_reverse_iterator等工具避免重复实现 - 迭代器特征萃取:利用
std::iterator_traits编写泛型代码 - SSO优化:对小容器考虑静态存储迭代器
7.4 现代C++的简化模式
C++20引入的range概念和views可以替代许多传统迭代器场景:
cpp复制// 传统方式
std::vector<int> process(const std::vector<int>& input) {
std::vector<int> result;
std::copy_if(input.begin(), input.end(), std::back_inserter(result),
[](int x) { return x % 2 == 0; });
std::transform(result.begin(), result.end(), result.begin(),
[](int x) { return x * x; });
return result;
}
// C++20 ranges方式
auto process(const std::vector<int>& input) {
return input | std::views::filter([](int x) { return x % 2 == 0; })
| std::views::transform([](int x) { return x * x; });
}
8. 从迭代器模式到C++概念(Concepts)
C++20的概念系统为迭代器类别提供了更严格的编译时检查:
cpp复制template<std::input_iterator Iter>
void processSequence(Iter begin, Iter end) {
// 保证Iter至少是输入迭代器
while (begin != end) {
auto&& value = *begin;
// 处理value...
++begin;
}
}
template<std::random_access_iterator Iter>
void fastProcess(Iter begin, Iter end) {
// 可以使用随机访问特性
auto mid = begin + (end - begin) / 2;
// ...快速处理逻辑
}
这种基于概念的编程方式比传统的标签派发或SFINAE更清晰可靠,代表了迭代器模式在现代C++中的演进方向。
