精读C++20设计模式——行为型设计模式:迭代器模式

精读C++20设计模式------行为型设计模式:迭代器模式

前言

​ 标准库就是再好不过的例子了,到处都是迭代器(憋笑),很显然,在组合模式的时候,我们经常有这样的需求:需要遍历一个集合(数组、链表、树、图、数据库结果集等),但又不希望暴露集合的内部表示或把遍历逻辑散落在调用方代码中。把遍历逻辑和集合实现耦合,会导致难以维护、难以替换底层数据结构,并破坏单一职责。这个时候使用迭代器,显然就是合适的。

什么是迭代器

迭代器模式(Iterator Pattern) 的核心思想是:为访问一个集合对象的元素提供一个顺序访问的方法,而不暴露该对象的内部表示 。它把"如何遍历"从"被遍历对象"中抽离出来,提供统一的访问接口。C++ 标准库(std::vector::iterator / std::map::iterator)就是迭代器模式的实际应用。

一个例子:二叉树的迭代遍历(中序)

cpp 复制代码
#include <iostream>
#include <stack>
#include <memory>

// 简单二叉树节点
template<typename T>
struct TreeNode {
    T val;
    TreeNode* left = nullptr;
    TreeNode* right = nullptr;
    TreeNode(T v): val(v) {}
};

// 中序迭代器(外部迭代器)
template<typename T>
class InorderIterator {
public:
    using Node = TreeNode<T>;

    // 构造为从 root 的最左节点开始
    InorderIterator(Node* root) { pushLeft(root); }

    // 默认构造表示 end()
    InorderIterator() = default;

    T& operator*() { return stack_.top()->val; }

    // 前缀 ++
    InorderIterator& operator++() {
        Node* node = stack_.top();
        stack_.pop();
        if (node->right) pushLeft(node->right);
        return *this;
    }

    bool operator!=(const InorderIterator& other) const {
        return !(*this == other);
    }

    bool operator==(const InorderIterator& other) const {
        if (stack_.empty() && other.stack_.empty()) return true;
        if (stack_.empty() != other.stack_.empty()) return false;
        return stack_.top() == other.stack_.top();
    }

private:
    std::stack<Node*> stack_;
    void pushLeft(Node* node) {
        while (node) {
            stack_.push(node);
            node = node->left;
        }
    }
};

// 二叉树包装,提供 begin/end
template<typename T>
class BinaryTree {
public:
    using Node = TreeNode<T>;
    BinaryTree() : root_(nullptr) {}
    ~BinaryTree() { /* 生产环境请实现 delete 逻辑或用 smart pointers */ }

    void setRoot(Node* r) { root_ = r; }

    InorderIterator<T> begin() { return InorderIterator<T>(root_); }
    InorderIterator<T> end() { return InorderIterator<T>(); }

private:
    Node* root_;
};

// 演示
int main() {
    // 构造一个测试树
    //      4
    //     / \
    //    2   6
    //   / \ / \
    //  1  3 5  7
    auto n1 = new TreeNode<int>(1);
    auto n3 = new TreeNode<int>(3);
    auto n5 = new TreeNode<int>(5);
    auto n7 = new TreeNode<int>(7);
    auto n2 = new TreeNode<int>(2); n2->left = n1; n2->right = n3;
    auto n6 = new TreeNode<int>(6); n6->left = n5; n6->right = n7;
    auto n4 = new TreeNode<int>(4); n4->left = n2; n4->right = n6;

    BinaryTree<int> tree;
    tree.setRoot(n4);

    for (auto it = tree.begin(); it != tree.end(); ++it) {
        std::cout << *it << " ";
    }
    std::cout << std::endl;

    // 简单清理(为示例目的)
    delete n1; delete n3; delete n5; delete n7; delete n2; delete n6; delete n4;
    return 0;
}

书中还提到了更牛的协程版本的调度,本人不太理解协程,但是为了尊重原书的内容,这里笔者没法做精读,只好照猫画虎了:

cpp 复制代码
#include <coroutine>
#include <exception>
#include <iostream>
#include <memory>
#include <optional>

// 简单 Generator<T>(教学用,非生产级)
template<typename T>
struct Generator {
    struct promise_type {
        std::optional<T> current_value;
        std::suspend_always yield_value(T value) {
            current_value = std::move(value);
            return {};
        }
        std::suspend_always initial_suspend() { return {}; }
        std::suspend_always final_suspend() noexcept { return {}; }
        Generator get_return_object() {
            return Generator{ std::coroutine_handle<promise_type>::from_promise(*this) };
        }
        void return_void() {}
        void unhandled_exception() { std::terminate(); }
    };

    using handle_type = std::coroutine_handle<promise_type>;

    explicit Generator(handle_type h) : coro(h) {}
    ~Generator() { if (coro) coro.destroy(); }

    // 简单的迭代接口(仅演示)
    bool next() {
        if (!coro || coro.done()) return false;
        coro.resume();
        return !coro.done();
    }
    T getValue() {
        return *coro.promise().current_value;
    }

private:
    handle_type coro;
};

// 二叉树节点(同上)
template<typename T>
struct Node {
    T val;
    Node* left = nullptr;
    Node* right = nullptr;
    Node(T v): val(v) {}
};

// 中序协程实现
template<typename T>
Generator<T> inorder_generator(Node<T>* root) {
    if (!root) co_return;
    for (auto v : inorder_generator(root->left)) {
        co_yield v; // 先产出左子树
    }
    co_yield root->val;   // 再产出根
    for (auto v : inorder_generator(root->right)) {
        co_yield v;
    }
}

// 但上面的 for-range 不能直接用,因为我们的 Generator 没有提供 range 支持。
// 这里改成直接递归的 yield(不使用 range-for)
template<typename T>
Generator<T> inorder_gen_simple(Node<T>* node) {
    if (!node) co_return;
    co_yield std::coroutine_handle<typename Generator<T>::promise_type>::from_promise(*(
        std::addressof(node->left), typename Generator<T>::promise_type()
    )); // 这里仅为示意 ------ 实际中需直接写递归 co_yield

    // 更清晰办法(直接递归写法):
    if (node->left) {
        auto g = inorder_gen_simple(node->left);
        // 手动迭代 g:需要更完整的 Generator 支持,这里不赘述
    }
    co_yield node->val;
    if (node->right) {
        auto g = inorder_gen_simple(node->right);
        // 手动迭代 g
    }
}
扩展:常见的方案对比表(易读版)
方案 典型场景 优点 缺点
外部迭代器(STL) 内存集合、本地算法 灵活、并发迭代、与算法兼容 实现复杂、需要客户端管理
内部迭代器(forEach) 简单遍历、封装操作 API 简洁,集合控制遍历 不易暂停、组合性差
协程/生成器 递归遍历、按需序列 代码直观、惰性、易写复杂遍历 需协程支持/库、实现复杂
复合迭代器(树等) 树/文件系统 扁平化复杂结构、可多策略 需额外栈/队列,较复杂
适配器(Filter/Map) 流式处理 可组合、模块化 多层包装有性能影响
分片/并行迭代器 大数据/并行处理 并行加速 一致性/合并复杂
批量/分页迭代器 网络/数据库 IO 高效、少往返 复杂的游标管理与一致性问题

总结

我们遇到什么问题?

当需要遍历不同类型的集合(数组、链表、树、图、数据库结果等)时,如何做到不暴露集合内部实现,同时为不同集合提供统一、可复用、可扩展的遍历接口?直接让客户端知道集合内部结构会导致高耦合、维护成本上升、难以复用。

迭代器模式如何解决?
  • 把"访问集合元素"的责任从集合本身中抽离出来,提供一个统一的访问接口(迭代器)。
  • 客户端依赖迭代器接口而不是集合的内部表示,从而解耦。
  • 通过不同的迭代器实现,支持多种遍历策略(顺序、反向、按层、条件过滤、并行分片等)。
各变种优劣(要点回顾)
  • 外部迭代器(STL):灵活、可与算法组合、支持并发迭代;但实现要求高、客户端需管理流程。
  • 内部迭代器(forEach):API 简单,便于封装;但不利于复杂控制与暂停/恢复。
  • 生成器/协程:代码直观且能很好表达递归遍历、懒加载;但依赖协程特性或第三方库,且实现细节复杂。
  • 复合/树形迭代器:适合把层次结构"扁平化"输出;但状态管理(栈/队列)和内存细节需要处理。
  • 适配器链(filter/map):非常适用于流式处理与组合,但包装层多会带来一定开销。
  • 并行/分片/分页迭代器:适合大数据与网络场景,提高吞吐但复杂度高。)。您更偏好哪一种?
相关推荐
尘似鹤2 小时前
微信小程序学习(三)补充
学习·微信小程序
Le1Yu2 小时前
2025-9-28学习笔记
java·笔记·学习
小欣加油2 小时前
leetcode 1863 找出所有子集的异或总和再求和
c++·算法·leetcode·职场和发展·深度优先
yuxb733 小时前
Ceph 分布式存储学习笔记(三):块存储和对象存储管理
笔记·ceph·学习
yuxb733 小时前
Ceph 分布式存储学习笔记(一):介绍、部署与集群配置(上)
笔记·ceph·学习
00后程序员张4 小时前
从零构建 gRPC 跨语言通信:C++ 服务端与
开发语言·c++
爱凤的小光5 小时前
图漾相机C++语言---Sample_V1(4.X.X版本)完整参考例子(待完善)
开发语言·c++·数码相机
GoldenaArcher5 小时前
Postman 学习笔记 IV:Workflow、Newman 与 Mock Server 实战技巧
笔记·学习·postman
知识分享小能手6 小时前
微信小程序入门学习教程,从入门到精通,微信小程序常用API(下)——知识点详解 + 案例实战(5)
前端·javascript·学习·微信小程序·小程序·vue·前端开发