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

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

前言

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

什么是迭代器

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

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

#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;
                                      }

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

#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):非常适用于流式处理与组合,但包装层多会带来一定开销。
  • 并行/分片/分页迭代器:适合大数据与网络场景,提高吞吐但复杂度高。)。您更偏好哪一种?
posted @ 2025-10-18 10:07  wzzkaifa  阅读(5)  评论(0)    收藏  举报