迭代器模式及优化
迭代器模式(Iterator Pattern)是一种行为型设计模式,用于提供一种统一的方式遍历聚合对象(如集合、容器)中的元素,而无需暴露对象的内部实现细节。它将遍历逻辑与聚合对象分离,使得遍历操作可以独立于聚合结构变化,同时支持对同一聚合对象进行多种不同的遍历方式。
一、介绍
核心角色
迭代器模式包含4个关键角色:
- 迭代器(Iterator)
定义遍历元素的接口,通常包含hasNext()
(是否有下一个元素)、next()
(获取下一个元素)等方法。 - 具体迭代器(ConcreteIterator)
实现迭代器接口,负责管理遍历的当前位置,记录遍历状态。 - 聚合(Aggregate)
定义创建迭代器的接口(通常是createIterator()
方法),充当迭代器的工厂。 - 具体聚合(ConcreteAggregate)
实现聚合接口,返回具体迭代器实例,同时维护元素的存储结构(如数组、链表等)。
优点
- 封装性好:客户端无需知道聚合对象的内部结构(如数组、链表),只需通过迭代器接口遍历。
- 单一职责:聚合对象专注于存储数据,迭代器专注于遍历逻辑,符合单一职责原则。
- 扩展性强:新增遍历方式只需添加新的迭代器类,无需修改聚合对象(符合开闭原则)。
- 统一接口:不同聚合对象可以提供相同的迭代器接口,客户端可以用一致的代码遍历不同容器。
适用场景
迭代器模式适用于以下场景:
- 需要遍历聚合对象但不暴露其内部结构
如遍历链表、树、哈希表等,客户端无需知道元素的存储方式。 - 需要为同一聚合对象提供多种遍历方式
如正序遍历、倒序遍历、过滤遍历(只遍历满足条件的元素)。 - 需要统一不同聚合对象的遍历接口
如让数组、链表、栈等不同容器使用相同的遍历方式(如STL中的begin()
/end()
)。 - 遍历逻辑复杂,需要与聚合对象分离
将遍历代码从聚合类中抽离,降低类的复杂度。
二、实现
以图书库为例,展示如何使用迭代器模式遍历不同类型的书架:
#include <iostream>
#include <vector>
#include <string>
#include <memory>
#include <stdexcept>// 前向声明
template <typename T>
class Iterator;// 抽象聚合类:定义创建迭代器的接口
template <typename T>
class Aggregate {
public:virtual ~Aggregate() = default;virtual std::unique_ptr<Iterator<T>> createIterator() = 0;virtual void add(const T& item) = 0;virtual size_t size() const = 0;virtual T get(size_t index) const = 0;
};// 抽象迭代器类:定义遍历接口
template <typename T>
class Iterator {
public:virtual ~Iterator() = default;virtual bool hasNext() const = 0;virtual T next() = 0;virtual void reset() = 0;
};// 具体聚合:书架(使用数组存储)
class BookShelf : public Aggregate<std::string> {
private:std::vector<std::string> books_;public:void add(const std::string& book) override {books_.push_back(book);}size_t size() const override {return books_.size();}std::string get(size_t index) const override {if (index >= books_.size()) {throw std::out_of_range("索引超出范围");}return books_[index];}// 创建数组迭代器std::unique_ptr<Iterator<std::string>> createIterator() override;
};// 具体迭代器:书架迭代器(数组遍历)
class BookShelfIterator : public Iterator<std::string> {
private:const BookShelf& shelf_;size_t currentIndex_;public:explicit BookShelfIterator(const BookShelf& shelf) : shelf_(shelf), currentIndex_(0) {}bool hasNext() const override {return currentIndex_ < shelf_.size();}std::string next() override {if (!hasNext()) {throw std::out_of_range("没有更多书籍");}return shelf_.get(currentIndex_++);}void reset() override {currentIndex_ = 0;}
};// 实现书架的迭代器创建方法
std::unique_ptr<Iterator<std::string>> BookShelf::createIterator() {return std::make_unique<BookShelfIterator>(*this);
}// 具体聚合:环形书架(循环遍历)
class CircularBookShelf : public Aggregate<std::string> {
private:std::vector<std::string> books_;public:void add(const std::string& book) override {books_.push_back(book);}size_t size() const override {return books_.size();}std::string get(size_t index) const override {if (books_.empty()) {throw std::runtime_error("书架为空");}// 环形索引(取模运算实现循环)return books_[index % books_.size()];}// 创建环形迭代器std::unique_ptr<Iterator<std::string>> createIterator() override;
};// 具体迭代器:环形迭代器(支持循环遍历)
class CircularIterator : public Iterator<std::string> {
private:const CircularBookShelf& shelf_;size_t currentIndex_;size_t maxSteps_; // 最大遍历步数(避免无限循环)public:explicit CircularIterator(const CircularBookShelf& shelf, size_t maxSteps = 10): shelf_(shelf), currentIndex_(0), maxSteps_(maxSteps) {}bool hasNext() const override {return currentIndex_ < maxSteps_ && !shelf_.size() == 0;}std::string next() override {if (!hasNext()) {throw std::out_of_range("已达到最大遍历步数");}return shelf_.get(currentIndex_++);}void reset() override {currentIndex_ = 0;}
};// 实现环形书架的迭代器创建方法
std::unique_ptr<Iterator<std::string>> CircularBookShelf::createIterator() {return std::make_unique<CircularIterator>(*this);
}// 客户端代码:统一遍历不同类型的书架
void printBooks(Iterator<std::string>& iterator) {while (iterator.hasNext()) {std::cout << "- " << iterator.next() << std::endl;}
}int main() {// 普通书架BookShelf normalShelf;normalShelf.add("《设计模式》");normalShelf.add("《C++ Primer》");normalShelf.add("《算法导论》");std::cout << "普通书架的书籍:" << std::endl;auto normalIterator = normalShelf.createIterator();printBooks(*normalIterator);// 环形书架CircularBookShelf circularShelf;circularShelf.add("《红楼梦》");circularShelf.add("《西游记》");circularShelf.add("《三国演义》");std::cout << "\n环形书架的书籍(循环遍历5本):" << std::endl;auto circularIterator = circularShelf.createIterator();printBooks(*circularIterator);return 0;
}
输出结果
普通书架的书籍:
- 《设计模式》
- 《C++ Primer》
- 《算法导论》环形书架的书籍(循环遍历5本):
- 《红楼梦》
- 《西游记》
- 《三国演义》
- 《红楼梦》
- 《西游记》
应用场景
- 容器类库
- C++ STL、Java Collections Framework等都广泛使用迭代器模式
- 如
std::vector<int>::iterator
、list.iterator()
- 数据库操作
- 数据库查询结果集(ResultSet)通过迭代器遍历记录
- 支持游标移动、条件过滤等复杂遍历
- 树形结构遍历
- 二叉树的前序、中序、后序遍历可通过不同迭代器实现
- XML/JSON文档解析中的节点遍历
- 流处理
- 输入流、输出流的内容读取(如
std::istream_iterator
) - 日志文件的逐行读取
- 输入流、输出流的内容读取(如
- UI组件遍历
- 界面控件树的遍历(如遍历所有按钮并设置属性)
- 菜单层级结构的遍历
三、优化
优化点
- STL风格迭代器
- 实现了符合STL标准的迭代器接口,包含
operator*
、operator++
等必要操作符 - 定义了正确的迭代器分类(
forward_iterator_tag
、bidirectional_iterator_tag
) - 支持
begin()
/end()
和rbegin()
/rend()
接口,与STL容器用法一致
- 实现了符合STL标准的迭代器接口,包含
- 范围for循环支持
- 通过实现
begin()
和end()
方法,使自定义容器支持C++11的范围for循环 - 客户端代码可以像遍历
vector
一样遍历自定义书架:for (const auto& book : shelf)
- 通过实现
- 迭代器扩展
- 增加了反向迭代器(
ReverseIterator
),支持从后向前遍历 - 实现了筛选迭代器(
FilteredIterator
),支持按条件过滤元素(结合了装饰器模式)
- 增加了反向迭代器(
- 类型安全与封装
- 使用
const
限定符确保常量正确性 - 将迭代器作为内部类实现,增强封装性
- 通过异常处理处理越界访问等错误情况
- 使用
- 数据模型完善
- 引入
Book
类封装实际数据,使示例更贴近真实场景 - 为
Book
类实现operator<<
,方便打印输出
- 引入
#include <iostream>
#include <vector>
#include <string>
#include <memory>
#include <stdexcept>
#include <algorithm>
#include <iterator>// 1. 书籍类(存储实际数据)
class Book {
private:std::string title_;std::string author_;public:Book(std::string title, std::string author): title_(std::move(title)), author_(std::move(author)) {}const std::string& getTitle() const { return title_; }const std::string& getAuthor() const { return author_; }// 重载输出运算符,方便打印friend std::ostream& operator<<(std::ostream& os, const Book& book) {return os << "\"" << book.title_ << "\" by " << book.author_;}
};// 2. 抽象书架类(聚合接口)
class BookShelf {
public:virtual ~BookShelf() = default;virtual void add(const Book& book) = 0;virtual size_t size() const = 0;virtual const Book& get(size_t index) const = 0;// STL风格迭代器接口class Iterator {public:using iterator_category = std::forward_iterator_tag;using value_type = const Book;using difference_type = std::ptrdiff_t;using pointer = const Book*;using reference = const Book&;Iterator(const BookShelf& shelf, size_t index): shelf_(shelf), index_(index) {}reference operator*() const {return shelf_.get(index_);}pointer operator->() const {return &shelf_.get(index_);}Iterator& operator++() {if (index_ < shelf_.size()) {++index_;}return *this;}Iterator operator++(int) {Iterator temp = *this;++(*this);return temp;}bool operator==(const Iterator& other) const {return &shelf_ == &other.shelf_ && index_ == other.index_;}bool operator!=(const Iterator& other) const {return !(*this == other);}private:const BookShelf& shelf_;size_t index_;};// 反向迭代器class ReverseIterator {public:using iterator_category = std::bidirectional_iterator_tag;using value_type = const Book;using difference_type = std::ptrdiff_t;using pointer = const Book*;using reference = const Book&;ReverseIterator(const BookShelf& shelf, size_t index): shelf_(shelf), index_(index) {}reference operator*() const {return shelf_.get(index_);}pointer operator->() const {return &shelf_.get(index_);}ReverseIterator& operator++() {if (index_ > 0) {--index_;} else {index_ = shelf_.size(); // 标记为结束}return *this;}ReverseIterator operator++(int) {ReverseIterator temp = *this;++(*this);return temp;}bool operator==(const ReverseIterator& other) const {return &shelf_ == &other.shelf_ && index_ == other.index_;}bool operator!=(const ReverseIterator& other) const {return !(*this == other);}private:const BookShelf& shelf_;size_t index_;};// 迭代器范围接口(支持范围for循环)Iterator begin() const { return Iterator(*this, 0); }Iterator end() const { return Iterator(*this, size()); }ReverseIterator rbegin() const { return size() > 0 ? ReverseIterator(*this, size() - 1) : rend(); }ReverseIterator rend() const { return ReverseIterator(*this, size()); }
};// 3. 具体聚合:线性书架
class LinearBookShelf : public BookShelf {
private:std::vector<Book> books_;public:void add(const Book& book) override {books_.push_back(book);}size_t size() const override {return books_.size();}const Book& get(size_t index) const override {if (index >= books_.size()) {throw std::out_of_range("索引超出范围");}return books_[index];}
};// 4. 具体聚合:环形书架(循环迭代)
class CircularBookShelf : public BookShelf {
private:std::vector<Book> books_;size_t maxIterations_; // 最大迭代次数(避免无限循环)public:explicit CircularBookShelf(size_t maxIterations = 2): maxIterations_(maxIterations) {}void add(const Book& book) override {books_.push_back(book);}size_t size() const override {return books_.empty() ? 0 : books_.size() * maxIterations_;}const Book& get(size_t index) const override {if (books_.empty()) {throw std::runtime_error("书架为空");}// 环形索引计算return books_[index % books_.size()];}
};// 5. 筛选迭代器(装饰器模式扩展)
class FilteredIterator {
private:BookShelf::Iterator it_;BookShelf::Iterator end_;std::function<bool(const Book&)> predicate_;// 找到下一个符合条件的元素void findNext() {while (it_ != end_ && !predicate_(*it_)) {++it_;}}public:FilteredIterator(BookShelf::Iterator begin, BookShelf::Iterator end,std::function<bool(const Book&)> predicate): it_(begin), end_(end), predicate_(std::move(predicate)) {findNext();}const Book& operator*() const {if (it_ == end_) {throw std::out_of_range("没有符合条件的元素");}return *it_;}FilteredIterator& operator++() {if (it_ != end_) {++it_;findNext();}return *this;}bool operator!=(const FilteredIterator& other) const {return it_ != other.it_;}
};// 筛选迭代器的范围包装
struct FilteredRange {BookShelf::Iterator begin_;BookShelf::Iterator end_;std::function<bool(const Book&)> predicate_;FilteredIterator begin() const {return FilteredIterator(begin_, end_, predicate_);}FilteredIterator end() const {return FilteredIterator(end_, end_, predicate_);}
};// 创建筛选范围的辅助函数
FilteredRange filter(const BookShelf& shelf, std::function<bool(const Book&)> predicate) {return {shelf.begin(), shelf.end(), std::move(predicate)};
}// 客户端代码
int main() {// 创建线性书架并添加书籍LinearBookShelf linearShelf;linearShelf.add(Book("设计模式", " Erich Gamma"));linearShelf.add(Book("C++ Primer", "Stanley Lippman"));linearShelf.add(Book("算法导论", "Thomas H. Cormen"));linearShelf.add(Book("C++ 并发编程", "Anthony Williams"));std::cout << "=== 线性书架(正向遍历) ===" << std::endl;for (const auto& book : linearShelf) {std::cout << "- " << book << std::endl;}std::cout << "\n=== 线性书架(反向遍历) ===" << std::endl;for (auto it = linearShelf.rbegin(); it != linearShelf.rend(); ++it) {std::cout << "- " << *it << std::endl;}// 创建环形书架CircularBookShelf circularShelf(2); // 循环2次circularShelf.add(Book("红楼梦", "曹雪芹"));circularShelf.add(Book("西游记", "吴承恩"));circularShelf.add(Book("三国演义", "罗贯中"));std::cout << "\n=== 环形书架(循环遍历) ===" << std::endl;for (const auto& book : circularShelf) {std::cout << "- " << book << std::endl;}// 筛选迭代器示例:只显示C++相关书籍std::cout << "\n=== 筛选C++相关书籍 ===" << std::endl;auto cppBooks = filter(linearShelf, [](const Book& book) {return book.getTitle().find("C++") != std::string::npos;});for (const auto& book : cppBooks) {std::cout << "- " << book << std::endl;}return 0;
}
输出结果
=== 线性书架(正向遍历) ===
- "设计模式" by Erich Gamma
- "C++ Primer" by Stanley Lippman
- "算法导论" by Thomas H. Cormen
- "C++ 并发编程" by Anthony Williams=== 线性书架(反向遍历) ===
- "C++ 并发编程" by Anthony Williams
- "算法导论" by Thomas H. Cormen
- "C++ Primer" by Stanley Lippman
- "设计模式" by Erich Gamma=== 环形书架(循环遍历) ===
- "红楼梦" by 曹雪芹
- "西游记" by 吴承恩
- "三国演义" by 罗贯中
- "红楼梦" by 曹雪芹
- "西游记" by 吴承恩
- "三国演义" by 罗贯中=== 筛选C++相关书籍 ===
- "C++ Primer" by Stanley Lippman
- "C++ 并发编程" by Anthony Williams
优化后优势
- 与STL生态兼容
- 可以使用STL算法库中的函数(如
std::for_each
、std::find
)操作自定义容器 - 开发者无需学习新的迭代器用法,降低学习成本
- 可以使用STL算法库中的函数(如
- 使用便捷性提升
- 范围for循环使遍历代码更简洁易读
- 反向迭代器和筛选迭代器提供了更丰富的遍历方式
- 扩展性更强
- 可以轻松添加新的迭代器类型(如随机访问迭代器、深度优先迭代器)
- 筛选迭代器展示了如何通过组合模式扩展迭代器功能
- 更符合现代C++规范
- 使用智能指针管理资源,避免内存泄漏
- 利用lambda表达式实现灵活的筛选条件
- 常量迭代器确保数据访问的const正确性