1. 迭代器模式概述
迭代器模式(Iterator Pattern)是GoF 23种设计模式中最常用的行为型模式之一。它提供了一种方法顺序访问一个聚合对象中的各个元素,而又不暴露该对象的内部表示。在实际开发中,我们经常会遇到需要遍历集合对象的情况,迭代器模式正是为解决这类问题而生的。
提示:STL中的vector、list等容器都内置了迭代器实现,这是迭代器模式最典型的应用场景。
迭代器模式的核心价值在于:
- 解耦集合对象的遍历行为与集合本身
- 提供统一的遍历接口,支持多种遍历方式
- 隐藏集合内部实现细节,增强封装性
2. 迭代器模式结构解析
2.1 模式类图
迭代器模式主要包含以下角色:
- Iterator(抽象迭代器):定义访问和遍历元素的接口
- ConcreteIterator(具体迭代器):实现迭代器接口,记录当前遍历位置
- Aggregate(抽象聚合类):定义创建迭代器对象的接口
- ConcreteAggregate(具体聚合类):实现创建迭代器对象的接口
cpp复制// 抽象迭代器示例
class Iterator {
public:
virtual void first() = 0;
virtual void next() = 0;
virtual bool isDone() = 0;
virtual T currentItem() = 0;
};
2.2 模式协作流程
- ConcreteAggregate创建并返回一个ConcreteIterator实例
- 客户端通过ConcreteIterator接口遍历ConcreteAggregate中的元素
- 迭代器内部维护当前遍历位置,通过聚合对象接口获取元素
3. C++实现详解
3.1 聚合类实现
聚合类负责存储数据和创建对应的迭代器。以下是关键实现要点:
cpp复制// aggregate.h
class Aggregate {
public:
virtual Iterator* createIterator() = 0;
virtual T getItem(int index) = 0;
virtual int size() = 0;
};
// concrete_aggregate.h
class ConcreteAggregate : public Aggregate {
public:
Iterator* createIterator() override {
return new ConcreteIterator(this);
}
T getItem(int index) override {
if(index < size())
return data[index];
throw std::out_of_range("Index out of bounds");
}
int size() override { return data.size(); }
private:
std::vector<T> data;
};
3.2 迭代器实现
迭代器类封装了遍历逻辑,核心要点包括:
cpp复制// iterator.h
class ConcreteIterator : public Iterator {
public:
ConcreteIterator(Aggregate* agg) : agg(agg), index(0) {}
void first() override { index = 0; }
void next() override {
if(!isDone())
++index;
}
bool isDone() override {
return index >= agg->size();
}
T currentItem() override {
return agg->getItem(index);
}
private:
Aggregate* agg;
int index;
};
3.3 使用示例
cpp复制int main() {
Aggregate* agg = new ConcreteAggregate();
Iterator* it = agg->createIterator();
for(it->first(); !it->isDone(); it->next()) {
std::cout << it->currentItem() << std::endl;
}
delete it;
delete agg;
return 0;
}
4. 迭代器模式高级应用
4.1 多态迭代器
通过抽象迭代器接口,可以实现针对不同聚合类的统一遍历方式:
cpp复制void traverse(Iterator* it) {
for(it->first(); !it->isDone(); it->next()) {
process(it->currentItem());
}
}
4.2 双向迭代器
扩展迭代器接口支持双向遍历:
cpp复制class BidirectionalIterator : public Iterator {
public:
virtual void prev() = 0;
virtual bool isBegin() = 0;
};
4.3 线程安全迭代器
在多线程环境下使用时需要考虑线程安全问题:
cpp复制class ThreadSafeIterator {
public:
T currentItem() {
std::lock_guard<std::mutex> lock(mutex);
return agg->getItem(index);
}
// 其他方法...
private:
std::mutex mutex;
};
5. 迭代器模式实践要点
5.1 设计考量
- 迭代器所有权:明确迭代器由谁创建、由谁销毁
- 遍历算法:考虑是否需要支持多种遍历方式(正序、逆序、过滤等)
- 失效处理:聚合对象修改时迭代器可能失效,需要设计处理机制
5.2 性能优化
- 内联小函数:频繁调用的first()、next()等函数应该内联
- 缓存友好:迭代器访问模式应该尽量利用CPU缓存
- 避免虚函数:性能关键路径上可以考虑CRTP模式避免虚函数开销
5.3 常见问题
-
迭代器失效:
- 解决方案1:使用迭代器时禁止修改集合
- 解决方案2:实现COW(Copy-On-Write)机制
-
嵌套迭代:
cpp复制// 错误示例:同一集合嵌套迭代 for(it1->first(); !it1->isDone(); it1->next()) { for(it2->first(); !it2->isDone(); it2->next()) { // 可能导致未定义行为 } } -
内存管理:
- 明确迭代器生命周期
- 考虑使用智能指针管理迭代器
6. 迭代器模式与其他模式的关系
6.1 与组合模式
组合模式常与迭代器模式结合使用,用于遍历树形结构:
cpp复制class CompositeIterator : public Iterator {
public:
CompositeIterator(Component* root) {
stack.push(root->createIterator());
}
// 实现迭代器接口...
};
6.2 与访问者模式
访问者模式通过迭代器遍历对象结构:
cpp复制void ObjectStructure::accept(Visitor* visitor) {
for(auto it = createIterator(); !it->isDone(); it->next()) {
it->currentItem()->accept(visitor);
}
}
6.3 与工厂方法模式
聚合类通常使用工厂方法创建迭代器:
cpp复制class Aggregate {
public:
virtual Iterator* createIterator() = 0; // 工厂方法
};
7. 现代C++中的迭代器
7.1 STL迭代器
STL定义了五种迭代器类别:
- 输入迭代器(Input Iterator)
- 输出迭代器(Output Iterator)
- 前向迭代器(Forward Iterator)
- 双向迭代器(Bidirectional Iterator)
- 随机访问迭代器(Random Access Iterator)
7.2 基于范围的for循环
C++11引入了基于范围的for循环,底层依赖迭代器:
cpp复制for(auto& item : container) {
// 等价于
// for(auto it=begin(container); it!=end(container); ++it)
}
7.3 迭代器特性(iterator_traits)
STL提供了iterator_traits用于获取迭代器特性:
cpp复制template<class Iterator>
void algorithm(Iterator first, Iterator last) {
using value_type = typename std::iterator_traits<Iterator>::value_type;
// ...
}
8. 迭代器模式最佳实践
- 遵循单一职责原则:将遍历算法与集合实现分离
- 接口最小化:迭代器接口应该尽可能简单
- 支持多种遍历:可以为同一集合提供不同迭代器
- 考虑const迭代器:提供对const集合的遍历支持
- 异常安全:确保迭代器操作不会破坏集合状态
实际项目中,迭代器模式最常见的应用场景包括:
- 封装复杂数据结构的遍历逻辑
- 提供统一接口遍历异构集合
- 实现惰性求值(如生成器模式)
- 支持并行遍历算法
在多年的开发实践中,我发现合理使用迭代器模式可以显著提高代码的可维护性和扩展性。特别是在开发通用库或框架时,良好的迭代器设计能让API更加清晰易用。一个经验法则是:当你发现自己在重复编写相似的遍历代码时,就应该考虑引入迭代器模式了。
