突破编程_C++_设计模式(迭代模式)

1 迭代模式的基本概念

在 C++ 中,迭代模式是一种常见的设计模式,它用于遍历或处理集合中的元素。迭代模式允许程序员在不了解集合内部表示的情况下,以一种统一和一致的方式来访问集合中的元素。这种模式的核心是迭代器对象,它封装了访问集合元素的逻辑。

以下是 C++ 中迭代模式的基本概念:

(1)迭代器(Iterator):
迭代器是一个对象,它定义了访问和遍历集合中元素的方法。
迭代器通常提供了诸如 begin(), end(), next(), previous(), value() 等方法,用于开始遍历、结束遍历、前进到下一个元素、回到前一个元素以及获取当前元素的值。
迭代器通常设计为轻量级对象,以便在遍历大型集合时不会造成过多的性能开销。

(2)可迭代对象(Iterable):
可迭代对象是一个支持迭代的集合或数据结构。
它通常提供一个或多个迭代器对象,用于访问其内部的元素。
在 C++ 中,常见的可迭代对象包括数组、向量(std::vector)、列表(std::list)、集合(std::set)等标准库容器。

(3)迭代范围:
迭代范围由迭代器对象定义,通常通过 begin() 和 end() 方法获取。
begin() 方法返回一个指向集合中第一个元素的迭代器,而 end() 方法返回一个表示集合末尾的迭代器(注意,这个迭代器并不指向任何有效的元素,而是作为一个哨兵值使用)。

2 迭代模式的实现步骤

迭代模式的实现步骤如下:

(1)定义迭代器接口:
首先,需要定义一个迭代器接口,它规定了迭代器对象应提供的方法。这通常包括获取当前元素的方法(如 operator*或 value()),移动到下一个元素的方法(如 operator++ 或 next()),以及比较迭代器是否相等或是否达到末尾的方法(如 operator== 或 operator!=)。

(2)实现迭代器类:
接下来,实现具体的迭代器类,该类继承或实现上述迭代器接口。迭代器类需要能够访问并遍历集合中的元素。这通常意味着迭代器内部需要保存一个指向集合中当前元素的指针或引用。

(3)在可迭代对象中提供迭代器:
可迭代对象(如容器类)需要提供方法来获取迭代器。通常,这包括 begin() 方法返回指向集合第一个元素的迭代器,以及 end() 方法返回指向集合末尾之后的迭代器(哨兵值)。

(4)使用迭代器遍历集合:
最后,客户端代码可以使用返回的迭代器来遍历集合。这通常通过循环实现,循环条件是比较迭代器是否等于 end() 返回的迭代器。

如下为样例代码:

#include <iostream>  
#include <memory>  // 假设有一个简单的集合类  
class MyCollection {
public:// 提供获取迭代器的方法  class Iterator {public:Iterator(std::unique_ptr<int[]>& data, int offset = 0){current = data.get() + offset;}// 前置递增操作符  Iterator& operator++() {++current;return *this;}// 解引用操作符  int& operator*() const {return *current;}// 相等操作符  bool operator==(const Iterator& other) const {return current == other.current;}// 不相等操作符  bool operator!=(const Iterator& other) const {return !(*this == other);}private:int* current; // 指向当前元素的指针  };MyCollection(size_t size){data.reset(new int[size]);this->size = size;}// 获取指向集合开始的迭代器  Iterator begin() {return Iterator(data);}// 获取指向集合末尾之后的迭代器(哨兵值)  Iterator end() {return Iterator(data, size);}// ... 其他方法和成员变量 ...  private:std::unique_ptr<int[]> data; // 指向集合数据的指针  int size;  // 集合的大小  
};int main() 
{// 假设有一个MyCollection的实例  MyCollection collection(5); // 初始化集合...  // 使用迭代器遍历集合,修改元素int offset = 0;for (MyCollection::Iterator it = collection.begin(); it != collection.end(); ++it) {*it = offset;offset++;}// 使用迭代器遍历集合,打印元素for (MyCollection::Iterator it = collection.begin(); it != collection.end(); ++it) {std::cout << *it << ' ';}std::cout << std::endl;return 0;
}

上面代码的输出为:

0 1 2 3 4

在这个例子中,MyCollection 类有一个内嵌的 Iterator 类,它实现了迭代器的基本功能。MyCollection 提供了 begin() 和 end() 方法来分别获取指向集合开始和末尾之后的迭代器。在 main() 函数中,我们使用这些迭代器来遍历修改并打印集合中的元素。

3 迭代模式的应用场景

C++迭代模式的应用场景非常广泛,主要涉及到需要顺序访问集合对象元素的情况,而无需关心集合对象的底层表示。以下是一些具体的应用场景:

(1)遍历聚合对象: 当需要遍历一个聚合对象(如数组、向量、列表等)并访问其元素时,可以使用迭代器模式。这种模式允许程序员在不了解聚合对象内部表示的情况下,以统一和一致的方式来访问元素。

(2)隐藏内部表示: 当不希望暴露聚合对象的内部表示时,迭代器模式非常有用。通过提供迭代器接口,可以隐藏聚合对象的实现细节,从而保护其不被外部代码直接访问和修改。

(3)支持多种遍历方式: 有时,对于同一个聚合对象,可能需要提供多种遍历方式。例如,可以按照顺序遍历,也可以按照逆序遍历,或者根据某种特定条件进行遍历。迭代器模式可以方便地支持这些不同的遍历需求。

(4)为不同聚合结构提供统一接口: 当存在多个具有不同内部实现的聚合对象,并且需要以一种统一的方式遍历它们时,迭代器模式非常有用。通过为这些聚合对象提供统一的迭代器接口,客户端代码可以一致地操作这些聚合对象,而无需关心它们的内部实现差异。

(5)软件框架和库: 在设计和实现软件框架和库时,迭代器模式也经常被使用。例如,C++ 标准库中的容器类(如 std::vector、std::list、std::set 等)都提供了迭代器接口,以便用户可以方便地遍历和访问容器中的元素。

3.1 迭代模式应用于遍历聚合对象

在 C++ 中,迭代模式通常应用于遍历聚合对象,即那些包含多个元素的集合或容器。通过迭代模式,我们可以提供一种统一的方式来访问和操作这些聚合对象中的元素,而无需关心聚合对象的具体实现细节。

C++ 标准库提供了多种聚合对象,如 std::vector、std::list、std::map 等,它们各自都支持迭代操作。这些聚合对象都提供了迭代器类型,使得开发者可以使用迭代器来遍历聚合对象中的元素。

下面是一个使用 C++ 迭代模式遍历聚合对象的示例:

#include <iostream>  
#include <vector>  int main() 
{// 创建一个聚合对象:std::vector<int>  std::vector<int> numbers = { 1, 2, 3, 4, 5 };// 使用迭代器遍历聚合对象  for (std::vector<int>::iterator it = numbers.begin(); it != numbers.end(); ++it) {// 通过迭代器访问元素  std::cout << *it << ' ';}std::cout << std::endl;// C++11 及以后版本还可以使用基于范围的for循环来简化迭代  for (const auto& num : numbers) {std::cout << num << ' ';}std::cout << std::endl;return 0;
}

上面代码的输出为:

1 2 3 4 5
1 2 3 4 5

在这个例子中,创建了一个 std::vector<int> 类型的聚合对象numbers,它包含 5 个整数。然后,使用迭代器 std::vector<int>::iterator 来遍历这个聚合对象。迭代器 it 从 numbers.begin()开始,直到达到 numbers.end() 为止。在每次迭代中,通过解引用迭代器*it来访问当前指向的元素,并将其打印出来。

从 C++11 开始,还可以使用基于范围的 for 循环(range-based for loop)来简化迭代过程。这种循环会自动处理迭代器的创建和递增,使得代码更加简洁易读。在上面的例子中,第二个 for 循环就展示了这种用法。

无论是使用传统的迭代器还是基于范围的 for 循环,C++ 迭代模式都提供了一种统一且灵活的方式来遍历聚合对象中的元素。这使得我们可以编写出更加通用和可维护的代码,而无需关心聚合对象的具体实现细节。

3.2 迭代模式应用于隐藏内部表示

以 数据库查询结果集的应用为例:在数据库应用中,查询操作通常返回一个结果集,它包含了满足查询条件的所有记录。这些记录可能以复杂的方式存储在数据库中,但应用程序通常只需要遍历和处理这些记录。通过应用 C++ 的迭代模式,可以隐藏数据库查询结果集的内部表示,并提供一个统一的接口来遍历结果。

以下是一个简化的示例,展示了如何使用迭代模式来遍历数据库查询结果集:

#include <iostream>  
#include <vector>  
#include <memory>  
#include <string>  // 假设的数据库记录类  
class DatabaseRecord {
public:DatabaseRecord(int id, const std::string& name) : id(id), name(name) {}// 输出记录信息  void print() const {std::cout << "ID: " << id << ", Name: " << name << std::endl;}int id;std::string name;// ... 其他字段 ...  
};// 数据库查询结果集的迭代器类  
class DatabaseResultIterator {
public:DatabaseResultIterator(std::vector<DatabaseRecord>::iterator begin, std::vector<DatabaseRecord>::iterator end): it(begin), end(end) {}// 解引用运算符,返回当前记录的引用  DatabaseRecord& operator*() {return *it;}// 前置递增运算符,移动到下一个记录  DatabaseResultIterator& operator++() {++it;return *this;}// 判断是否到达结果集末尾  bool operator==(const DatabaseResultIterator& other) const {return it == other.it;}bool operator!=(const DatabaseResultIterator& other) const {return !(*this == other);}// 打印数据 void print() {std::cout << "id: " << (*it).id << "; name: " << (*it).name << std::endl;}private:std::vector<DatabaseRecord>::iterator it;std::vector<DatabaseRecord>::iterator end;
};// 数据库查询结果集类  
class DatabaseResultSet {
public:// 构造函数,这里应该是从数据库获取数据的逻辑  DatabaseResultSet() {// 示例数据填充  records.emplace_back(1, "Alice");records.emplace_back(2, "Bob");records.emplace_back(3, "Charlie");}// 返回指向第一个记录的迭代器  DatabaseResultIterator begin() {return DatabaseResultIterator(records.begin(), records.end());}// 返回指向结果集末尾之后位置的迭代器  DatabaseResultIterator end() {return DatabaseResultIterator(records.end(), records.end());}private:std::vector<DatabaseRecord> records; // 假设这是从数据库获取的记录列表  
};int main() 
{// 模拟从数据库获取结果集  DatabaseResultSet resultSet;// 使用迭代器遍历结果集  for (DatabaseResultIterator it = resultSet.begin(); it != resultSet.end(); ++it) {it.print(); // 输出每条记录的信息  }return 0;
}

上面代码的输出为:

id: 1; name: Alice
id: 2; name: Bob
id: 3; name: Charlie

在这个示例中,DatabaseRecord 类表示数据库中的一条记录。DatabaseResultIterator 类是一个迭代器,它封装了对 std::vector<DatabaseRecord>的迭代操作。DatabaseResultSet 类表示查询结果集,它内部使用 std::vector<DatabaseRecord> 来存储从数据库获取的记录。通过实现 begin() 和 end() 方法,DatabaseResultSet 提供了一个迭代器接口来遍历结果集中的记录。

在 main() 函数中,模拟从数据库获取了一个结果集,并使用迭代器来遍历和打印每条记录的信息。由于迭代器的存在,所以不需要直接访问 DatabaseResultSet 内部的 std::vector<DatabaseRecord>,从而实现了对内部表示的隐藏。

这个示例展示了 C++ 迭代模式如何应用于隐藏数据库查询结果集的内部表示。通过提供统一的迭代器接口,可以使代码更加清晰、可维护和可移植,同时保持对内部实现的封装性。

4 迭代模式的优点与缺点

C++ 迭代模式的优点主要包括:

(1)简化代码: 迭代模式通过提供一个统一的接口来遍历聚合对象,从而简化了代码。这使得遍历逻辑可以被集中管理,减少了代码的重复性,提高了代码的可读性和可维护性。

(2)隐藏内部实现: 迭代模式允许隐藏聚合对象的内部表示,从而保护其内部数据的完整性和安全性。外部代码只能通过迭代器来访问聚合对象,而无需了解其内部实现细节。

(3)支持多种遍历方式: 迭代器模式可以轻松支持多种遍历方式,如正序遍历、倒序遍历等。这使得我们可以根据实际需求提供不同的迭代器实现,满足不同的遍历需求。

(4)易于扩展: 在迭代模式中,增加新的聚合类和迭代器类都很方便,无需修改原有代码。这符合开闭原则,使得系统更加灵活和可扩展。

然而,C++ 迭代模式也存在一些缺点:

(1)增加类的个数: 由于迭代模式将存储数据和遍历数据的职责分离,因此增加新的聚合类通常需要对应增加新的迭代器类。这在一定程度上增加了系统的复杂性,可能导致类的个数成对增加。

(2)可能降低性能: 在某些情况下,使用迭代器模式可能会引入额外的性能开销。例如,每次遍历都需要创建迭代器对象,这可能会增加内存消耗和处理器负载。

(3)学习成本: 对于不熟悉迭代模式的开发者来说,理解和实现迭代器可能需要一定的学习成本。此外,过度使用迭代器模式也可能导致代码结构变得复杂和难以理解。

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

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

相关文章

「CISP题库精讲」CISP题库习题解析精讲20道

前言 本篇主要对CISP教材第九章《计算环境安全》的一些习题进行讲解&#xff0c;包括20道题&#xff0c;这里只是部分习题&#xff0c;针对第九章可能会多写几章的内容&#xff0c;如果我发布的这些习题里面没有你想找的那道题&#xff0c;你也可以直接私信我&#xff0c;我加…

「连载」边缘计算(二十九)03-11:边缘部分源码(源码分析篇)

&#xff08;接上篇&#xff09; EdgeCore之matamanager 前面对EdgeCore组件的edged、devicetwin、edgehub、eventbus功能模块进行了分析&#xff0c;本节对EdgeCore组件的另一个功能模块metamanager进行剖析。metamanager作为EdgeCore中的edged模块与edgehub模块进行交互的桥…

求根节点到叶节点数字之和

题目链接 求根节点到叶节点数字之和 题目描述 注意点 树中节点的数目在范围 [1, 1000] 内0 < Node.val < 9树的深度不超过10 解答思路 深度优先遍历计算从根节点到叶子节点组成的所有数字&#xff08;每向下一层乘以10&#xff09;&#xff0c;再计算所有的数字之和…

中小型生产企业工业数据采集分析平台 规划生产流程

工业数据采集分析平台是一款优秀的工控自动化软件&#xff0c;可以用于数据采集、实时监测和过程控制、数据传输、系统联动、远程监控等多种应用&#xff0c;数据采集平台通过对设备运行状态及相关参数监视实现保证每个环节都能按照既定方案进行&#xff0c;同时缩短非正常停机…

shiro整合thymeleaf(接上一篇抛出的问题)

在上一篇末尾&#xff0c;讲到如何实现不同身份的用户&#xff0c;有不同的权限&#xff0c;从而看到不同的页面&#xff0c;下面我们就来实现下这个功能 1.导入依赖 <!--shiro整合thymeleaf--><dependency><groupId>com.github.theborakompanioni</group…

python(ogr)处理geojson为本地shp文件

前言 本次所利用的geojson数据来自https://geo.datav.aliyun.com/areas_v3/bound/410000_full.json &#xff0c;如果觉得下方代码看起来不方便&#xff0c;可以来GitHub上来看&#xff0c;在这上面还有一些辅助内容便于理解 GISpjd/GIS-union-Python (github.com)https://gi…

14.WEB渗透测试--Kali Linux(二)

免责声明&#xff1a;内容仅供学习参考&#xff0c;请合法利用知识&#xff0c;禁止进行违法犯罪活动&#xff01; 内容参考于&#xff1a; 易锦网校会员专享课 上一个内容&#xff1a;13.WEB渗透测试--Kali Linux&#xff08;一&#xff09;-CSDN博客 netcat简介内容:13.WE…

精品基于Springboot的体育用品租赁租用管理系统的设计与实现

《[含文档PPT源码等]精品基于Springboot的体育用品管理系统的设计与实现[包运行成功]》该项目含有源码、文档、PPT、配套开发软件、软件安装教程、项目发布教程、包运行成功&#xff01; 软件开发环境及开发工具&#xff1a; Java——涉及技术&#xff1a; 前端使用技术&…

c# DbHelper的封装

c# DbHelper的封装 基于ADO.NET框架&#xff0c;封装了适用于多个关系型数据库的DbHelper。通过简洁明了的代码&#xff0c;实现了对各种数据库的高效操作。 public class DbHelper{private readonly DataBase _dataBase;public DbHelper(DataBase dataBase){_dataBase data…

SpringCloud-实现基于RabbitMQ的消息队列

消息队列是现代分布式系统中常用的通信机制&#xff0c;用于在不同的服务之间传递消息。在Spring Cloud框架中&#xff0c;我们可以利用RabbitMQ实现强大而可靠的消息队列系统。本篇博客将详细介绍如何在Spring Cloud项目中集成RabbitMQ&#xff0c;并创建一个简单的消息队列。…

【Kotlin】类和对象

1 前言 Kotlin 是面向对象编程语言&#xff0c;与 Java 语言类似&#xff0c;都有类、对象、属性、构造函数、成员函数&#xff0c;都有封装、继承、多态三大特性&#xff0c;不同点如下。 Java 有静态&#xff08;static&#xff09;代码块&#xff0c;Kotlin 没有&#xff1…

Spring AOP常见面试题

目录 一、对于AOP的理解 二、Spring是如何实现AOP的 1、execution表达式 2、annotation 3、基于Spring API&#xff0c;通过xml配置的方式。 4、基于代理实现 三、Spring AOP的实现原理 四、Spring是如何选择使用哪种动态代理 1、Spring Framework 2、Spring Boot 五…

博士推荐 | 纤维与聚合物科学博士,功能性纺织品研发主管

编辑 / 木子 审核 / 朝阳 伟骅英才 伟骅英才致力于以大数据、区块链、AI人工智能等前沿技术打造开放的人力资本生态&#xff0c;用科技解决职业领域问题&#xff0c;提升行业数字化服务水平&#xff0c;提供创新型的产业与人才一体化服务的人力资源解决方案和示范平台&#x…

二分查找【详解】

本期介绍&#x1f356; 主要介绍&#xff1a;二分查找的简单思路&#xff0c;为什么必须在有序的前提下才能使用二分查找&#xff0c;该怎么用C程序来实现二分查找&#xff0c;二分查找的局限性&#x1f440;。 文章目录 1. 题目2. 思路3. 前提条件4. 编写程序 1. 题目 在一个有…

选择性遗忘可以帮助人工智能学得更好?

最近&#xff0c;一些计算机科学家创建了一种更灵活、更灵巧的机器学习模型。诀窍在于&#xff1a;它必须定期忘记它所知道的信息。虽然这种新方法不会取代支撑最大应用程序的庞大模型&#xff0c;但它能揭示这些程序如何理解语言的更多信息。 &#xff08;PS&#xff1a;如果…

日常007:alias给长命令起个简短的别名

alias 命令可以给那些太长的命令起一个简短的别名。便于记忆&#xff0c;提高输入效率。 用法和示例 1. 创建别名 创建别名&#xff0c;使用语法alias new_commandoriginal_command&#xff0c;例如 alias llls -lF使用别名 $ ll total 32 -rw-r--r-- 1 soulio soulio 1…

【动态规划】代码随想录算法训练营第四十六天 |139.单词拆分,关于多重背包,你该了解这些! ,背包问题总结篇!(待补充)

139.单词拆分 1、题目链接&#xff1a;. - 力扣&#xff08;LeetCode&#xff09; 2、文章讲解&#xff1a;代码随想录 3、题目&#xff1a; 给定一个非空字符串 s 和一个包含非空单词的列表 wordDict&#xff0c;判定 s 是否可以被空格拆分为一个或多个在字典中出现的单词…

【Docker】Node 容器部署及配置参数

Node.js环境标准软件基于Bitnami node 构建。当前版本为20.11.1 你可以通过轻云UC部署工具直接安装部署&#xff0c;也可以手动按如下文档操作&#xff0c;该项目已经全面开源&#xff0c;可以从如下环境获取 配置文件地址: https://gitee.com/qingplus/qingcloud-platform Q…

解决报错The following packages have unmet dependencies

一、问题描述 终端输入&#xff1a; nvcc -V 提示&#xff1a; Command nvcc not found, but can be installed with:sudo apt install nvidia-cuda-toolkit按照提示安装&#xff0c;终端输入 sudo apt install nvidia-cuda-toolkit&#xff0c;报错内容如下&#xff1a; …

3分钟彻底搞懂Web UI自动化测试之【POM设计模式】

为什么要用POM设计模式 前期&#xff0c;我们学会了使用PythonSelenium编写Web UI自动化测试线性脚本 线性脚本&#xff08;以快递100网站登录举例&#xff09;&#xff1a; import time from selenium import webdriver from selenium.webdriver.common.by import By dri…