设计模式(C++)详解——迭代器模式(4)

<摘要>

迭代器模式是设计模式中"行为型模式"的核心成员,它像一位"智能仓库管理员"------让我们无需知道仓库(聚合对象,如数组、链表、容器)内部如何存放货物(元素),只需通过管理员(迭代器)就能按顺序取出所有货物。其核心价值在于解耦"聚合对象的存储逻辑"与"元素的遍历逻辑",不仅让代码更灵活(同一遍历方式适配不同容器),还让聚合对象的内部结构更安全(无需暴露细节)。

在C++世界里,迭代器模式更是"顶流"------STL(标准模板库)的"容器-算法-迭代器"三大支柱中,迭代器是连接容器与算法的桥梁:vector、list等容器通过begin()/end()提供迭代器,sort、for_each等算法通过迭代器操作容器元素,实现了"算法与容器无关"的奇迹。本文将从"仓库提货"的小故事切入,带大家吃透迭代器模式的起源、设计、实现与应用,甚至亲手写一个书架迭代器,感受它如何解决传统遍历的"痛点"。


<解析>

一、从"仓库提货"说起:迭代器模式的诞生背景

1.1 没有迭代器时,我们是怎么"提货"的?

假设你是一家书店老板,有两个"仓库"存书:

  • 仓库A(数组):书按顺序堆在一排架子上,每个架子有编号(下标);
  • 仓库B(链表):书用绳子串起来,每本书都知道下一本在哪(指针)。

某天你让员工去提货,员工得这么做:

cpp 复制代码
// 提仓库A(数组)的书:必须知道"下标"
void getBooksFromArray(Book arr[], int size) {
    for (int i = 0; i < size; i++) { // 依赖数组的"下标特性"
        std::cout << arr[i].getName() << std::endl;
    }
}

// 提仓库B(链表)的书:必须知道"节点结构"
void getBooksFromList(Node* head) {
    Node* current = head;
    while (current != nullptr) { // 依赖链表的"指针特性"
        std::cout << current->book.getName() << std::endl;
        current = current->next;
    }
}

问题来了

  • 员工必须熟记两个仓库的"内部结构"(数组要下标、链表要指针),一旦仓库换成"栈"或"树",员工就得重新学新方法;
  • 若仓库升级(比如数组改成动态数组),所有提货代码都要改(比如size变成动态获取);
  • 仓库的内部细节完全暴露(员工知道数组下标、链表节点),安全性差。

这就是迭代器模式出现前的"遍历困境"------聚合对象(仓库)与遍历逻辑(提货方式)强耦合

1.2 迭代器:给所有仓库配"统一管理员"

为了解决这个问题,"迭代器模式"应运而生:我们给每个仓库配一位"管理员"(迭代器),管理员只对外提供两个简单接口:

  • "还有下一本书吗?"(hasNext());
  • "把下一本书给我"(next())。

员工提货时,只需问管理员这两个问题,完全不用管仓库里书是堆的、串的还是叠的。哪怕仓库换成火箭筒仓库,只要管理员不变,员工提货方式都不变!

这就是迭代器模式的核心思想:将遍历逻辑从聚合对象中抽离,用统一接口封装遍历行为

二、迭代器模式的核心概念:一张图看懂所有角色

2.1 核心角色(UML类图)

我们用Mermaid画一张"书架迭代器"的UML类图,清晰展示迭代器模式的5个核心角色(按规范检查3轮:全角括号、括号内双引号、语法合规):

classDiagram direction TB % 1. 抽象聚合接口:定义创建迭代器的方法 class "Aggregate(抽象聚合)" { + createIterator():Iterator* "创建迭代器" + ~Aggregate() "虚析构,确保子类释放" } % 2. 抽象迭代器接口:定义遍历方法 class "Iterator(抽象迭代器)" { + hasNext():bool "判断是否有下一个元素" + next():Book* "获取下一个元素" + ~Iterator() "虚析构,确保子类释放" } % 3. 具体聚合:实际的仓库(如书架) class "BookShelf(具体聚合-书架)" { - books:std::vector~Book~ "存储图书的容器" + addBook(book:Book):void "添加图书" + getBookAt(index:int):Book "获取指定位置图书" + getLength():int "获取图书总数" + createIterator():Iterator* "实现接口,创建书架迭代器" } % 4. 具体迭代器:仓库的管理员(书架管理员) class "BookShelfIterator(具体迭代器)" { - bookShelf:const BookShelf* "关联的书架" - index:int "当前遍历索引" + BookShelfIterator(shelf:const BookShelf*) "绑定书架,初始化索引" + hasNext():bool "判断是否有下一本书" + next():Book* "获取下一本书" } % 5. 元素:仓库里的货物(图书) class "Book(元素-图书)" { - name:std::string "图书名称" + getName():std::string "获取图书名称" } % 角色关系:继承、关联 "Aggregate(抽象聚合)" <|-- "BookShelf(具体聚合-书架)" : 继承(实现createIterator) "Iterator(抽象迭代器)" <|-- "BookShelfIterator(具体迭代器)" : 继承(实现遍历方法) "BookShelf(具体聚合-书架)" ..> "BookShelfIterator(具体迭代器)" : 关联(创建迭代器) "BookShelfIterator(具体迭代器)" ..> "Book(元素-图书)" : 关联(获取图书)
2.2 关键术语解读(通俗版)
术语 通俗解释
聚合对象(Aggregate) "仓库":存储多个元素的容器(如数组、链表、vector、书架),负责管理元素的存储。
迭代器(Iterator) "仓库管理员":负责遍历聚合对象的元素,对外隐藏仓库的内部结构。
hasNext() 管理员的"灵魂拷问":"后面还有货吗?",避免提货时"摸空"(越界访问)。
next() 管理员的"动作":"把下一个货给你",同时自己往前走一步(索引自增)。
const_iterator "只读管理员":只能看货、不能改货(比如员工只能提货、不能在仓库里涂改书名),避免误操作。

三、迭代器模式的设计意图:为什么要这么设计?

3.1 核心目标:解耦!解耦!解耦!

迭代器模式的终极目标是解耦"聚合对象"和"遍历逻辑",具体体现在三个方面:

  1. 聚合对象无需关心遍历:书架(BookShelf)只需要管"存书"(addBook),不用管"怎么拿书"------遍历逻辑全交给BookShelfIterator;
  2. 遍历逻辑可复用:如果有另一个"杂志架"(MagazineShelf),只要它的迭代器也实现Iterator接口,员工用同样的"问管理员"方式就能提货;
  3. 聚合对象内部结构安全:员工看不到书架用vector还是list存书(不用知道getBookAt或index),只能通过迭代器拿书,避免内部细节暴露。
3.2 设计权衡:灵活与复杂的平衡

没有完美的设计,迭代器模式也需要权衡:

设计优势 潜在挑战 解决方案
遍历接口统一(适配所有容器) 迭代器失效问题(如vector扩容后,原迭代器指向无效内存) 1. 遍历中避免修改容器;2. 使用"安全迭代器"(如Java的CopyOnWriteArrayList);3. C++中注意容器操作后的迭代器有效性。
聚合对象内部隐藏 实现复杂度增加(需写抽象接口+具体类) 1. 复用现有库(如C++ STL已实现所有迭代器);2. 简单场景可用"内部类"简化迭代器实现。
支持多种遍历方式(如正序、逆序) 多迭代器共存时的状态管理(如一个书架同时有正序和逆序管理员) 每个迭代器独立维护索引,互不干扰(如BookShelfIterator和ReverseBookShelfIterator)。

四、实例实战:亲手实现迭代器模式

我们分两个案例讲解:自定义书架迭代器 (理解原理)和C++ STL迭代器(实际应用),每个案例都带完整代码、注释和编译运行指南。

4.1 案例1:自定义书架迭代器(从0到1实现)
4.1.1 完整代码(带详细注释)
cpp 复制代码
#include <iostream>
#include <vector>
#include <string>
#include <stdexcept> // 用于抛出越界异常

/**
 * @brief 图书类(迭代器要遍历的元素)
 * 
 * 封装图书的核心属性(名称),提供只读访问方法,确保图书信息不被随意修改。
 * 角色:迭代器模式中的"元素(Element)"。
 * 
 * 成员变量说明:
 *   - name: 图书名称,私有成员,仅通过getName()访问
 * 
 * 成员函数说明:
 *   - Book(const std::string& bookName): 构造函数,初始化图书名称
 *   - getName() const: 获取图书名称,返回字符串,const修饰确保不修改对象
 */
class Book {
private:
    std::string name; // 图书名称,私有变量,隐藏实现细节
public:
    // 构造函数:创建图书时必须指定名称
    Book(const std::string& bookName) : name(bookName) {}
    
    // 获取图书名称:只读接口,外部无法修改name
    std::string getName() const {
        return name;
    }
};

/**
 * @brief 抽象聚合接口(Aggregate)
 * 
 * 定义所有聚合对象(如书架、杂志架)必须实现的接口,核心是创建迭代器。
 * 作用:统一聚合对象的"迭代器创建入口",让客户端无需关心具体聚合类型。
 * 
 * 成员函数说明:
 *   - createIterator() const: 纯虚函数,返回迭代器指针,子类必须实现
 *   - ~Aggregate(): 虚析构函数,确保子类对象销毁时,父类指针能正确调用子类析构
 */
class Aggregate {
public:
    virtual ~Aggregate() {} // 虚析构:避免内存泄漏的关键!
    virtual class Iterator* createIterator() const = 0; // 纯虚函数,创建迭代器
};

/**
 * @brief 抽象迭代器接口(Iterator)
 * 
 * 定义所有迭代器必须实现的遍历接口,客户端通过这两个方法遍历元素,
 * 无需知道聚合对象的内部结构。
 * 
 * 成员函数说明:
 *   - hasNext() const: 判断是否还有下一个元素,返回true/false
 *   - next(): 获取下一个元素,返回Book指针;若没有下一个元素,抛出异常
 *   - ~Iterator(): 虚析构函数,确保子类迭代器能正确销毁
 */
class Iterator {
public:
    virtual ~Iterator() {} // 虚析构:避免迭代器对象内存泄漏
    virtual bool hasNext() const = 0; // 纯虚函数:是否有下一个元素
    virtual Book* next() = 0; // 纯虚函数:获取下一个元素
};

/**
 * @brief 具体聚合:书架(BookShelf)
 * 
 * 实际的"仓库",用vector存储图书,实现Aggregate接口创建书架迭代器。
 * 角色:迭代器模式中的"具体聚合(ConcreteAggregate)"。
 * 
 * 成员变量说明:
 *   - books: 用vector存储图书,私有变量,外部无法直接访问
 * 
 * 成员函数说明:
 *   - addBook(const Book& book): 向书架添加图书,将book存入vector
 *   - getBookAt(int index) const: 按索引获取图书,仅提供给迭代器使用(内部接口)
 *   - getLength() const: 获取书架上图书总数,仅提供给迭代器使用(内部接口)
 *   - createIterator() const: 实现Aggregate接口,创建BookShelfIterator对象
 */
class BookShelf : public Aggregate {
private:
    std::vector<Book> books; // 存储图书的容器,私有,隐藏内部结构
public:
    // 向书架添加图书:外部唯一添加图书的接口
    void addBook(const Book& book) {
        books.push_back(book);
    }
    
    // 按索引获取图书:仅给迭代器调用,外部无法直接访问(保护内部结构)
    Book getBookAt(int index) const {
        // 边界检查:避免索引越界
        if (index < 0 || index >= static_cast<int>(books.size())) {
            throw std::out_of_range("BookShelf index out of range");
        }
        return books[index];
    }
    
    // 获取图书总数:给迭代器判断是否遍历结束
    int getLength() const {
        return static_cast<int>(books.size());
    }
    
    // 创建书架迭代器:实现抽象聚合的接口
    Iterator* createIterator() const override {
        // 将当前书架对象(this)传给迭代器,让迭代器能访问书架的图书
        return new BookShelfIterator(this);
    }
};

/**
 * @brief 具体迭代器:书架迭代器(BookShelfIterator)
 * 
 * 书架的"管理员",负责遍历书架上的图书,实现Iterator接口。
 * 角色:迭代器模式中的"具体迭代器(ConcreteIterator)"。
 * 
 * 成员变量说明:
 *   - bookShelf: 指向关联书架的指针,用于获取图书信息
 *   - index: 当前遍历的索引,初始为0(从第一本开始)
 * 
 * 成员函数说明:
 *   - BookShelfIterator(const BookShelf* shelf): 构造函数,绑定书架并初始化索引
 *   - hasNext() const: 判断当前索引是否小于图书总数(是否有下一本书)
 *   - next(): 获取当前索引的图书,然后索引自增;若没有下一本书,抛出异常
 */
class BookShelfIterator : public Iterator {
private:
    const BookShelf* bookShelf; // 关联的书架,const确保不修改书架
    int index; // 当前遍历的索引,初始为0
public:
    // 构造函数:绑定书架,初始化索引为0(从第一本开始遍历)
    BookShelfIterator(const BookShelf* shelf) : bookShelf(shelf), index(0) {}
    
    // 判断是否有下一本书:索引 < 图书总数 → 有下一本
    bool hasNext() const override {
        return index < bookShelf->getLength();
    }
    
    // 获取下一本书:先检查是否有下一本,再返回图书并自增索引
    Book* next() override {
        if (!hasNext()) {
            // 没有下一本书时抛出异常,避免返回无效指针
            throw std::runtime_error("No more books in BookShelf");
        }
        // 动态创建Book对象(避免返回局部对象引用导致悬空)
        Book* currentBook = new Book(bookShelf->getBookAt(index));
        index++; // 索引自增,下次获取下一本
        return currentBook;
    }
};

/**
 * @brief 主函数:演示书架迭代器的使用流程
 * 
 * 客户端使用迭代器的核心逻辑:
 * 1. 创建聚合对象(书架)→ 2. 向聚合对象添加元素(图书)→ 3. 获取迭代器(管理员)→ 4. 用迭代器遍历元素 → 5. 释放资源
 * 
 * 运行流程说明:
 *   - 先创建书架并添加4本经典书籍
 *   - 获取书架的迭代器,通过hasNext()和next()遍历所有图书
 *   - 遍历后释放迭代器和动态创建的Book对象,避免内存泄漏
 */
int main() {
    try {
        // 1. 创建书架(聚合对象)
        BookShelf myBookShelf;
        
        // 2. 向书架添加图书(元素)
        myBookShelf.addBook(Book("《C++ Primer 第6版》"));
        myBookShelf.addBook(Book("《算法导论 第3版》"));
        myBookShelf.addBook(Book("《设计模式:可复用面向对象软件的基础》"));
        myBookShelf.addBook(Book("《深入理解计算机系统 第3版》"));
        
        // 3. 获取迭代器(管理员):客户端无需知道是BookShelfIterator,只需用Iterator接口
        Iterator* shelfIterator = myBookShelf.createIterator();
        
        // 4. 遍历书架:客户端只调用hasNext()和next(),完全不关心书架用vector存储
        std::cout << "我的书架上有这些书:\n";
        while (shelfIterator->hasNext()) {
            Book* currentBook = shelfIterator->next();
            std::cout << "→ " << currentBook->getName() << "\n";
            delete currentBook; // 释放动态创建的Book对象,避免内存泄漏
        }
        
        // 5. 释放迭代器:避免迭代器对象内存泄漏
        delete shelfIterator;
    } catch (const std::exception& e) {
        // 捕获异常并打印错误信息,增强程序健壮性
        std::cerr << "程序出错:" << e.what() << std::endl;
        return 1; // 异常退出,返回非0状态码
    }
    
    return 0; // 正常退出
}
4.1.2 Makefile(编译运行指南)
makefile 复制代码
# Makefile:编译自定义书架迭代器代码
# 适用环境:Linux/macOS(需安装g++)

# 1. 编译器设置:使用g++,指定C++11标准(支持vector、异常等特性)
CXX = g++
CXXFLAGS = -std=c++11 -Wall -g  # -Wall:开启所有警告;-g:生成调试信息

# 2. 目标文件设置:可执行文件名、源文件、目标文件
TARGET = bookshelf_iterator  # 最终生成的可执行文件
SRCS = main.cpp              # 源文件(上面的代码保存为main.cpp)
OBJS = $(SRCS:.cpp=.o)       # 目标文件(自动将.cpp换成.o)

# 3. 默认目标:输入make时执行,编译生成可执行文件
all: $(TARGET)

# 4. 链接目标文件:将.o文件链接成可执行文件
$(TARGET): $(OBJS)
	$(CXX) $(CXXFLAGS) -o $@ $(OBJS)  # $@:目标文件(bookshelf_iterator);$<:依赖文件(.o)

# 5. 编译源文件:将.cpp编译成.o文件
%.o: %.cpp
	$(CXX) $(CXXFLAGS) -c $< -o $@  # -c:只编译不链接

# 6. 清理目标:输入make clean时执行,删除.o和可执行文件
clean:
	rm -f $(OBJS) $(TARGET)  # 强制删除,避免提示

# 7. 运行目标:输入make run时执行,编译后直接运行
run: all
	./$(TARGET)
4.1.3 编译运行步骤与结果解读
  1. 准备文件 :将上面的代码保存为main.cpp,Makefile保存为Makefile,放在同一文件夹;

  2. 编译代码 :打开终端,进入文件夹,输入make,终端会输出:

    复制代码
    g++ -std=c++11 -Wall -g -c main.cpp -o main.o
    g++ -std=c++11 -Wall -g -o bookshelf_iterator main.o

    此时文件夹会生成main.o(目标文件)和bookshelf_iterator(可执行文件);

  3. 运行程序 :输入make run,终端会输出:

    复制代码
    我的书架上有这些书:
    → 《C++ Primer 第6版》
    → 《算法导论 第3版》
    → 《设计模式:可复用面向对象软件的基础》
    → 《深入理解计算机系统 第3版》
  4. 结果解读

    • 客户端(main函数)完全没用到vectorindex等书架内部细节,只通过Iterator接口遍历;
    • 即使把书架的vector改成list,只要BookShelfgetBookAtgetLength方法不变,迭代器和客户端代码完全不用改------这就是解耦的威力!
4.2 案例2:C++ STL迭代器(实际开发中最常用)

C++ STL的"容器-算法-迭代器"模型,是迭代器模式的"终极应用"。比如vectorlistmap等容器,都通过begin()/end()提供迭代器,而sortfor_each等算法通过迭代器操作容器元素。

4.2.1 完整代码(演示STL迭代器的核心特性)
cpp 复制代码
#include <iostream>
#include <vector>
#include <list>
#include <map>
#include <algorithm>  // for_each算法
#include <iterator>   // 迭代器工具(如back_inserter)

/**
 * @brief 打印函数:用于for_each算法,打印单个元素
 * 
 * 演示STL算法如何通过迭代器"间接操作"容器元素,无需知道容器类型。
 */
template <typename T>
void printElement(const T& elem) {
    std::cout << elem << " ";
}

int main() {
    // --------------------------
    // 1. vector:随机访问迭代器(最灵活的迭代器)
    // 特点:支持++、--、+n、-n(像指针一样直接跳)
    // --------------------------
    std::vector<int> vec = {10, 20, 30, 40, 50};
    std::cout << "1. vector遍历(随机访问迭代器):\n";
    
    // 方式1:普通for循环(利用随机访问特性,直接定位到i位置)
    std::vector<int>::iterator vec_it1;
    for (vec_it1 = vec.begin(); vec_it1 != vec.end(); ++vec_it1) {
        *vec_it1 += 5; // 迭代器可修改元素(非const)
    }
    std::cout << "vector元素加5后:";
    for (vec_it1 = vec.begin(); vec_it1 != vec.end(); ++vec_it1) {
        std::cout << *vec_it1 << " "; // 输出:15 25 35 45 55
    }
    std::cout << "\n";
    
    // 方式2:随机访问(直接跳到第3个元素,begin()+2 → 索引2)
    std::vector<int>::iterator vec_it2 = vec.begin() + 2;
    std::cout << "vector第3个元素:" << *vec_it2 << "\n"; // 输出:35
    
    // --------------------------
    // 2. list:双向迭代器(仅支持前后移动,不支持跳)
    // 特点:支持++、--,但不支持+n(链表结构决定,无法直接跳)
    // --------------------------
    std::list<std::string> lst = {"苹果", "香蕉", "橙子", "葡萄"};
    std::cout << "\n2. list遍历(双向迭代器):\n";
    
    // 方式1:普通for循环(只能++,不能+2)
    std::list<std::string>::iterator lst_it1;
    std::cout << "list元素:";
    for (lst_it1 = lst.begin(); lst_it1 != lst.end(); ++lst_it1) {
        std::cout << *lst_it1 << " "; // 输出:苹果 香蕉 橙子 葡萄
    }
    std::cout << "\n";
    
    // 方式2:双向移动(从end()向前跳1步,指向最后一个元素)
    std::list<std::string>::iterator lst_it2 = lst.end();
    --lst_it2; // end()是"尾后位置",必须--才能指向最后一个元素
    std::cout << "list最后一个元素:" << *lst_it2 << "\n"; // 输出:葡萄
    
    // --------------------------
    // 3. map:双向迭代器(键值对容器,迭代器指向pair)
    // 特点:迭代器的value_type是std::pair<const Key, Value>,键不可修改
    // --------------------------
    std::map<int, std::string> student_map = {
        {101, "张三"},
        {102, "李四"},
        {103, "王五"}
    };
    std::cout << "\n3. map遍历(双向迭代器):\n";
    
    std::map<int, std::string>::iterator map_it;
    for (map_it = student_map.begin(); map_it != student_map.end(); ++map_it) {
        // map的迭代器指向pair,first是键(不可修改),second是值(可修改)
        std::cout << "学号:" << map_it->first << ",姓名:" << map_it->second << "\n";
    }
    
    // --------------------------
    // 4. STL算法:for_each(迭代器的"终极复用")
    // 特点:同一算法可适配所有容器,只要容器提供迭代器
    // --------------------------
    std::cout << "\n4. STL算法for_each遍历:\n";
    std::cout << "vector用for_each:";
    std::for_each(vec.begin(), vec.end(), printElement<int>); // 输出:15 25 35 45 55
    std::cout << "\n";
    
    std::cout << "list用for_each:";
    std::for_each(lst.begin(), lst.end(), printElement<std::string>); // 输出:苹果 香蕉 橙子 葡萄
    std::cout << "\n";
    
    // --------------------------
    // 5. const_iterator:只读迭代器(避免误修改元素)
    // 特点:只能读取元素,不能修改(*it = xxx 会报错)
    // --------------------------
    std::cout << "\n5. const_iterator(只读):\n";
    std::vector<int>::const_iterator const_vec_it;
    std::cout << "vector只读遍历:";
    for (const_vec_it = vec.cbegin(); const_vec_it != vec.cend(); ++const_vec_it) {
        // *const_vec_it = 100; // 错误!const_iterator不允许修改元素
        std::cout << *const_vec_it << " "; // 只能读取
    }
    std::cout << "\n";
    
    return 0;
}
4.2.2 Makefile与运行结果

将代码保存为stl_iterator.cpp,Makefile与案例1类似(只需修改TARGETSRCS):

makefile 复制代码
TARGET = stl_iterator
SRCS = stl_iterator.cpp
# 其余部分与案例1相同

运行make run后,输出如下:

复制代码
1. vector遍历(随机访问迭代器):
vector元素加5后:15 25 35 45 55 
vector第3个元素:35

2. list遍历(双向迭代器):
list元素:苹果 香蕉 橙子 葡萄 
list最后一个元素:葡萄

3. map遍历(双向迭代器):
学号:101,姓名:张三
学号:102,姓名:李四
学号:103,姓名:王五

4. STL算法for_each遍历:
vector用for_each:15 25 35 45 55 
list用for_each:苹果 香蕉 橙子 葡萄 

5. const_iterator(只读):
vector只读遍历:15 25 35 45 55 
4.2.3 关键知识点:STL迭代器的5种类型

STL根据迭代器的功能强弱,将其分为5类,不同容器对应不同迭代器,这也是STL算法适配不同容器的关键:

迭代器类型 支持的操作(核心) 典型容器 适用场景
输入迭代器 ++、*(只读)、==、!= istream_iterator 从容器读取元素(如文件读取)
输出迭代器 ++、*(只写) ostream_iterator 向容器写入元素(如文件写入)
前向迭代器 输入迭代器 + 多遍遍历 forward_list、unordered_map 单链表遍历,可重复遍历
双向迭代器 前向迭代器 + -- list、map、set 双向链表遍历,支持前后移动
随机访问迭代器 双向迭代器 + +n、-n、[]、<、> vector、deque、array 随机访问元素,效率最高

五、迭代器模式的现状与趋势:从"设计模式"到"语言特性"

迭代器模式早已不只是"设计模式手册里的概念",而是成为很多编程语言的"内置特性",让遍历变得更简单:

5.1 其他语言中的迭代器
语言 迭代器实现方式 示例代码(遍历列表)
Python 内置__iter__()__next__()方法,for循环自动调用迭代器 python books = ["C++", "Python"] for book in books: print(book)
Java Collection接口提供iterator()方法,foreach循环依赖迭代器 java List<String> books = new ArrayList<>(); for (String book : books) { System.out.println(book); }
C# IEnumerable接口提供GetEnumerator(),foreach自动遍历 csharp List<string> books = new List<string>(); foreach (var book in books) { Console.WriteLine(book); }
5.2 迭代器模式的新趋势
  1. 更简洁的遍历语法 :如C++11的"范围for循环"(for (auto& elem : container)),底层还是迭代器,但代码更短;
  2. 流式迭代器 :如Java的Stream API、C++20的Ranges库,迭代器与函数式编程结合,支持过滤、映射等操作(如vec | std::views::filter(x>10) | std::views::transform(x*2));
  3. 安全迭代器 :解决传统迭代器"失效"问题,如Java的CopyOnWriteArrayList,遍历期间修改容器不会导致迭代器失效(代价是拷贝内存)。

六、总结:迭代器模式的"灵魂"是什么?

迭代器模式的灵魂,不是"写一个Iterator接口",而是**"分工"**------让聚合对象专注于"存储",让迭代器专注于"遍历",就像:

  • 餐厅的后厨(聚合对象)只负责做菜(存储元素),不关心客人怎么吃;
  • 服务员(迭代器)负责把菜端给客人(遍历元素),客人(客户端)只需要吃,不用管后厨怎么做菜。

这种分工带来的好处,就是灵活性可复用性------换个后厨(比如从vector换成list),只要服务员(迭代器)不变,客人的用餐体验(客户端代码)就完全不受影响。

在C++ STL中,这种分工达到了极致:算法(客人的用餐方式)完全不依赖容器(后厨),只通过迭代器(服务员)操作元素,这也是STL能成为"C++基石"的重要原因。

如果你以后写代码时,发现"遍历一个容器需要写很多依赖内部结构的代码",不妨想想迭代器模式------给你的"仓库"配一位"管理员",让代码更简洁、更灵活!

附录:常见问题解答(FAQ)

问题 解答
为什么STL的vector迭代器可能失效? vector扩容时会重新分配内存,原迭代器指向的旧内存被释放,因此失效。解决办法:扩容后重新获取迭代器。
const_iterator和普通iterator的区别? const_iterator只能读取元素,不能修改;普通iterator可以修改元素。建议只读遍历时用const_iterator,避免误操作。
自己实现迭代器时,要注意什么? 1. 实现虚析构函数,避免内存泄漏;2. 做好边界检查(hasNext()判断),避免越界;3. 迭代器状态独立(每个迭代器有自己的index)。
迭代器模式和工厂模式有什么关系? 聚合对象的createIterator()方法,本质是"工厂方法"------每个聚合对象是一个工厂,创建对应的迭代器。
复制代码
相关推荐
大飞pkz2 小时前
【设计模式】状态模式
开发语言·设计模式·c#·状态模式
笨手笨脚の2 小时前
设计模式-建造者模式
java·设计模式·建造者模式·创建型设计模式
Jiezcode3 小时前
LeetCode 48. 旋转图像
c++·算法·leetcode·职场和发展
咔咔咔的3 小时前
165. 比较版本号
c++
charlie1145141913 小时前
精读C++20设计模式——创造型设计模式:单例模式
c++·学习·单例模式·设计模式·c++20
高山有多高3 小时前
C语言实战项目:贪吃蛇(2)
c语言·开发语言·数据结构·c++·算法·游戏·游戏设计
郝学胜-神的一滴3 小时前
现代C++ Lambda表达式:最佳实践、深入理解和资源推荐
开发语言·c++·程序人生·软件工程
1710orange4 小时前
java设计模式:工厂方法 + 建造者模式
java·设计模式
_OP_CHEN4 小时前
C++基础:(五)类和对象(下)—— static、友元和内部类
开发语言·c++·构造函数·static成员·友元·匿名对象·编译器优化