【C++设计模式】第十六篇:迭代器模式(Iterator)

注意:复现代码时,确保 VS2022 使用 C++17/20 标准以支持现代特性。

遍历聚合对象的统一方式


1. 模式定义与用途

核心思想

  • 迭代器模式:提供一种方法顺序访问聚合对象的元素,而无需暴露其内部表示。
  • 关键用途
    1.​统一遍历接口:为不同数据结构(如数组、链表、树)提供一致的遍历方式。
    ​2.支持多种遍历策略:前向、反向、条件过滤等。
    ​3.简化聚合类设计:将遍历逻辑从聚合类中分离。

经典场景

  • STL容器的迭代器(如std::vector::iterator)。
  • 自定义集合类(如链表、图)的遍历。
  • 数据库查询结果的逐行遍历。

2. 模式结构解析

UML类图

+---------------------+          +---------------------+  
|      Aggregate      |          |       Iterator       |  
+---------------------+          +---------------------+  
| + createIterator()  |<>------->| + next(): void       |  
+---------------------+          | + hasNext(): bool    |  ^                      +---------------------+  |                                ^  |                                |  +-------+-------+              +---------+---------+  |               |              |                   |  
+---------------------+    +-------------------+ +----------------+  
| ConcreteAggregate   |    |  ConcreteIterator | |     Client      |  
+---------------------+    +-------------------+ +----------------+  
| + createIterator()  |    | + next()          | | 通过迭代器遍历聚合对象 |  
+---------------------+    | + hasNext()       | +----------------+  +-------------------+  

角色说明

  1. Aggregate:聚合接口,定义创建迭代器的方法(如createIterator())。
  2. ConcreteAggregate:具体聚合类,实现迭代器创建逻辑。
  3. Iterator:迭代器接口,定义遍历方法(如next()hasNext())。
  4. ConcreteIterator:具体迭代器,实现特定遍历逻辑。
  5. Client:通过迭代器访问聚合对象,无需依赖其内部结构。

3. 现代C++实现示例

场景:自定义链表迭代器

步骤1:定义链表节点与聚合类
#include <iostream>  
#include <memory>  template <typename T>  
class ListNode {  
public:  T value;  std::shared_ptr<ListNode<T>> next;  ListNode(T val) : value(val), next(nullptr) {}  
};  // 聚合类:单向链表  
template <typename T>  
class LinkedList {  
public:  void append(T value) {  auto newNode = std::make_shared<ListNode<T>>(value);  if (!head_) {  head_ = newNode;  } else {  tail_->next = newNode;  }  tail_ = newNode;  }  // 创建正向迭代器  class Iterator;  Iterator begin() { return Iterator(head_); }  Iterator end() { return Iterator(nullptr); }  private:  std::shared_ptr<ListNode<T>> head_ = nullptr;  std::shared_ptr<ListNode<T>> tail_ = nullptr;  
};  
步骤2:实现迭代器类
template <typename T>  
class LinkedList<T>::Iterator {  
public:  Iterator(std::shared_ptr<ListNode<T>> node) : current_(node) {}  T& operator*() const { return current_->value; }  Iterator& operator++() {  if (current_) current_ = current_->next;  return *this;  }  bool operator!=(const Iterator& other) const {  return current_ != other.current_;  }  private:  std::shared_ptr<ListNode<T>> current_;  
};  
步骤3:客户端代码
int main() {  LinkedList<int> list;  list.append(1);  list.append(2);  list.append(3);  // 使用范围for循环(依赖begin()和end())  for (auto num : list) {  std::cout << num << " ";  // 输出:1 2 3  }  // 手动迭代  auto it = list.begin();  while (it != list.end()) {  std::cout << *it << " ";  ++it;  }  
}  
扩展:反向迭代器
template <typename T>  
class LinkedList<T>::ReverseIterator {  
public:  ReverseIterator(std::shared_ptr<ListNode<T>> head) {  // 遍历链表,将节点指针存入栈以实现反向  auto curr = head;  while (curr) {  stack_.push(curr);  curr = curr->next;  }  }  T& operator*() { return stack_.top()->value; }  ReverseIterator& operator++() {  if (!stack_.empty()) stack_.pop();  return *this;  }  bool operator!=(const ReverseIterator& other) {  return !stack_.empty() || !other.stack_.empty();  }  private:  std::stack<std::shared_ptr<ListNode<T>>> stack_;  
};  

4. 应用场景示例

场景1:树结构的深度优先遍历

class TreeNode {  
public:  int value;  std::vector<std::shared_ptr<TreeNode>> children;  
};  class DepthFirstIterator {  
public:  DepthFirstIterator(std::shared_ptr<TreeNode> root) {  stack_.push(root);  }  std::shared_ptr<TreeNode> next() {  auto node = stack_.top();  stack_.pop();  for (auto it = node->children.rbegin(); it != node->children.rend(); ++it) {  stack_.push(*it);  }  return node;  }  bool hasNext() { return !stack_.empty(); }  private:  std::stack<std::shared_ptr<TreeNode>> stack_;  
};  

场景2:过滤迭代器(条件遍历)

template <typename T, typename Predicate>  
class FilterIterator {  
public:  FilterIterator(typename LinkedList<T>::Iterator it, Predicate pred)  : it_(it), pred_(pred) {  // 找到第一个满足条件的元素  while (it_ != end_ && !pred_(*it_)) ++it_;  }  T& operator*() { return *it_; }  FilterIterator& operator++() {  do { ++it_; } while (it_ != end_ && !pred_(*it_));  return *this;  }  bool operator!=(const FilterIterator& other) { return it_ != other.it_; }  private:  typename LinkedList<T>::Iterator it_;  typename LinkedList<T>::Iterator end_;  Predicate pred_;  
};  // 使用示例:遍历链表中的偶数  
auto isEven = [](int x) { return x % 2 == 0; };  
FilterIterator<int, decltype(isEven)> begin(list.begin(), isEven);  
FilterIterator<int, decltype(isEven)> end(list.end(), isEven);  
while (begin != end) {  std::cout << *begin << " ";  ++begin;  
}  

5. 优缺点分析

​优点​缺点
解耦遍历逻辑与数据结构增加类的数量(迭代器与聚合类需配对)
支持多种遍历策略(正向、反向等)复杂数据结构迭代器实现成本高(如图遍历)
隐藏聚合对象内部实现部分语言/框架已内置迭代器(如STL)

6. 调试与优化策略

调试技巧(VS2022)​

1.​验证迭代器有效性:
  • 在迭代器越界时触发断言:
T& operator*() {  assert(current_ != nullptr && "迭代器越界!");  return current_->value;  
}  
2. ​检查迭代器状态:
  • operator++()中设置断点,观察指针移动是否符合预期。

性能优化

1. 预计算遍历路径:
  • 对树或图的遍历,预计算路径并缓存结果(如广度优先遍历队列)。
2. 内存连续性优化:
  • 使用std::vector存储节点,利用内存局部性提升遍历速度。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/32512.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

C语言实现队列数据结构:思路与代码详解

目录 一、引言 二、整体思路 三、代码模块分析 &#xff08;一&#xff09;头文件包含与宏定义 &#xff08;二&#xff09;数据类型定义 &#xff08;三&#xff09;队列操作函数 1. 队列初始化 2. 队列销毁 3. 入队操作 4. 出队操作 5. 获取队头元素 6…

商业智能BI的未来,如何看待AI+BI这种模式?

昨天在和一位朋友线上聊天的时候&#xff0c;提了一个问题&#xff0c;你是如何看待AI&#xff08;人工智能&#xff09;BI&#xff08;商业智能&#xff09;这种模式和方向的&#xff0c;我大概来说一下我个人的看法。 以我在商业智能BI项目中接触到的行业和企业&#xff0c;…

如何制作Windows系统盘、启动盘?(MediaCreationTool_22H2)

文章目录 每日一句正能量前言一、准备工作二、制作启动盘后记 每日一句正能量 每个在你生命里出现的人&#xff0c;都有原因。喜欢你的人给你温暖关心。你喜欢的人让你学会爱和付出&#xff0c;不喜欢你的人让你自省成长。你不喜欢的人教会你宽容尊重&#xff0c;没有人是偶然出…

DataWhale 大语言模型 - 语言模型发展历程

大语言模型 LLMBook 项目背景 本课程围绕中国人民大学高瓴人工智能学院赵鑫教授团队出品的《大语言模型》书籍展开&#xff0c;覆盖大语言模型训练与使用的全流程&#xff0c;从预训练到微调与对齐&#xff0c;从使用技术到评测应用&#xff0c;帮助学员全面掌握大语言模型的…

C#带有设备仿真功能串口调试助手

本文档介绍一种方法,可以用来仿真串口设备。这样调试PLC程序时可以在没有仪器时用于测试程序的运行。详细代码见: https://download.csdn.net/download/qq_34047402/90477066 C#带有设备仿真功能串口调试助手资源-CSDN文库 步骤如下: 1.把串口设备接收和发送仿真数据放到一…

本地部署 OpenManus 保姆级教程(Windows 版)

一、环境搭建 我的电脑是Windows 10版本&#xff0c;其他的没尝试&#xff0c;如果大家系统和我的不一致&#xff0c;请自行判断&#xff0c;基本上没什么大的出入啊。 openManus的Git地址&#xff1a;https://github.com/mannaandpoem/OpenManus 根据官网的两种安装推荐方式如…

01 | Go 项目开发极速入门课介绍

提示&#xff1a; 所有体系课见专栏&#xff1a;Go 项目开发极速入门实战课。 你好&#xff0c;欢迎学习本课程。本课程是一个 Go 项目开发极速入门课程。旨在帮助刚学习完 Go 基础语法的 Go 开发者&#xff0c;快速掌握如何开发一个功能相对全面的 Go 项目。 根据课程设计目标…

使用 Elastic-Agent 或 Beats 将 Journald 中的 syslog 和 auth 日志导入 Elastic Stack

作者&#xff1a;来自 Elastic TiagoQueiroz 我们在 Elastic 一直努力将更多 Linux 发行版添加到我们的支持矩阵中&#xff0c;现在 Elastic-Agent 和 Beats 已正式支持 Debian 12&#xff01; 本文演示了我们正在开发的功能&#xff0c;以支持使用 Journald 存储系统和身份验…

江科大51单片机笔记【15】直流电机驱动(PWM)

写在前言 此为博主自学江科大51单片机&#xff08;B站&#xff09;的笔记&#xff0c;方便后续重温知识 在后面的章节中&#xff0c;为了防止篇幅过长和易于查找&#xff0c;我把一个小节分成两部分来发&#xff0c;上章节主要是关于本节课的硬件介绍、电路图、原理图等理论…

【Linux】:封装线程

朋友们、伙计们&#xff0c;我们又见面了&#xff0c;本期来给大家带来封装线程相关的知识点&#xff0c;如果看完之后对你有一定的启发&#xff0c;那么请留下你的三连&#xff0c;祝大家心想事成&#xff01; C 语 言 专 栏&#xff1a;C语言&#xff1a;从入门到精通 数据结…

全球领先的光学方案设计公司:倚光科技

在光学技术革新的浪潮中&#xff0c;倚光&#xff08;深圳&#xff09;科技有限公司以创新者的姿态迅速崛起&#xff0c;成为全球光学领域的标杆企业。自 2021 年成立以来&#xff0c;公司始终聚焦纳米光学技术研发与超精密加工&#xff0c;凭借顶尖的技术实力和前瞻性的市场布…

2.2.3 TCP—UDP-QUIC

文章目录 2.2.3 TCP—UDP-QUIC1. TCP如何做到可靠性传输1. ACK机制2. 重传机制3. 序号机制4. 窗口机制5. 流量机制6. 带宽机制 2. tcp和udp如何选择1. tcp和udp格式对比2. ARQ协议&#xff08;Automatic Repeat reQuest&#xff0c;自动重传请求&#xff09;1. ARQ协议的主要类…

【动手实验】TCP 连接的建立与关闭抓包分析

本文是基于知识星球程序员踩坑案例分享中的作业进行的复现和总结&#xff0c;借此加深对 TCP 协议的理解&#xff0c; 原文参见TCP 连接的建立和关闭 —— 强烈建议新手看看。 实验环境 这里使用两台位于同一子网的腾讯云服务器&#xff0c;IP 分别是 node2&#xff08;172.1…

视频理解之Actionclip(论文宏观解读)

配合解读代码解读 1.研究背景 1. 视频行为识别的重要性 视频行为识别是视频理解领域的核心任务之一&#xff0c;旨在通过分析视频内容来识别和分类其中的人物行为或活动。这一任务在多个领域具有重要的应用价值&#xff0c;例如智能监控、人机交互、自动驾驶、医疗健康等。随…

基于LabVIEW的脚本化子VI动态生成

该示例展示了一种利用LabVIEW VI脚本&#xff08;VI Scripting&#xff09;技术&#xff0c;通过程序化方式动态生成并替换子VI的解决方案。核心逻辑为&#xff1a;基于预定义的模板VI&#xff0c;根据用户选择的数学操作&#xff08;加法或乘法&#xff09;&#xff0c;自动生…

Debian系统grub新增启动项

参考链接 给grub添加自定义启动项_linux grub定制 启动项名称自定义-CSDN博客 www.cnblogs.com 1. boot里面的grub.cfg 使用vim打开boot里面的grub.cfg sudo vim /boot/grub/grub.cfg 这时候会看到文件最上方的提示 2. 真正配置grub的文件 从刚才看到的文件提示中&#x…

ctfhub-web-SSRF通过攻略

1.URL Bypass 打开题目 请求的URL中必须包含http://notfound.ctfhub.com&#xff0c;使用来绕过即可 /?urlhttp://notfound.ctfhub.com127.0.0.1/flag.php 成功获得flag 2.数字IP Bypass 开启题目 发现127以及172被过滤了 尝试别的绕过 127.0.0.1–>localhost 127.0…

C语言(23)

字符串函数 11.strstr函数 1.1函数介绍&#xff1a; 头文件&#xff1a;string.h char *strstr ( const char * str1,const char *str2); 作用&#xff1a;在一个字符串&#xff08;str1&#xff09;中寻找另外一个字符串&#xff08;str2&#xff09;是否出现过 如果找到…

uniapp+Vue3 组件之间的传值方法

一、父子传值&#xff08;props / $emit 、ref / $refs&#xff09; 1、props / $emit 父组件通过 props 向子组件传递数据&#xff0c;子组件通过 $emit 触发事件向父组件传递数据。 父组件&#xff1a; // 父组件中<template><view class"container">…

晨控CK-FR08与汇川H5U系列PLC配置EtherNet/IP通讯连接手册

晨控CK-FR08与汇川H5U系列PLC配置EtherNet/IP通讯连接手册 晨控CK-FR08系列作为晨控智能工业级别RFID读写器,支持大部分工业协议如RS232、RS485、以太网。支持工业协议Modbus RTU、Modbus TCP、Profinet、EtherNet/lP、EtherCat以及自由协议TCP/IP等。 本期主题&#xff1a;围…