C++ STL(1)迭代器
文章目录
- 一、迭代器详解
- 1、迭代器的定义与功能
- 2、迭代器类型
- 3、示例
- 4、迭代器失效
- 4.1、`vector` 迭代器失效分析
- 4.2、`list` 迭代器失效分析
- 4.3、`set` 与 `map` 迭代器失效分析
- 5、总结
前言:
在C++标准模板库(STL)中,迭代器是一个核心概念,它提供了一种统一的方法来访问和遍历容器中的元素,而无需关心容器的具体类型。迭代器就像是一个智能指针,封装了对容器内部元素的访问方式,使得算法能够以一致的方式操作不同的容器。
一、迭代器详解
1、迭代器的定义与功能
迭代器本质上是一个对象,它支持指针的某些操作,如访问、解引用、递增和递减等。通过迭代器,可以遍历容器中的所有元素,读取或修改它们的值,甚至可以在某些情况下连接算法与容器,实现复杂的操作。
迭代器的主要功能包括:
- 遍历容器:通过迭代器可以遍历容器中的所有元素。例如,使用
begin()
和end()
方法获取容器的起始和结束迭代器,然后通过循环来访问每个元素。 - 访问元素:可以通过迭代器读取或修改容器中的元素。这通常是通过解引用迭代器(使用
*
操作符)来实现的。 - 连接算法与容器:STL中的很多算法(如排序、查找等)都是通过迭代器来操作容器的。迭代器为算法提供了一个统一的接口,使得算法可以独立于容器的具体实现。
2、迭代器类型
STL中定义了五种主要的迭代器类型,它们提供了不同级别的功能和灵活性:
- 输入迭代器(Input Iterators):这种迭代器用于从容器中读取数据。它只支持单向遍历,即只能向前移动(通过
++
操作符)。输入迭代器只能进行一次读取,读取后迭代器就会前进到下一个元素。 - 输出迭代器(Output Iterators):与输入迭代器相反,输出迭代器用于向容器中写入数据。它同样只支持单向遍历,且只能进行一次写入操作,写入后迭代器会自动前进到下一个位置。
- 前向迭代器(Forward Iterators):前向迭代器类似于输入和输出迭代器,但它支持多次读写操作。它也只能单向遍历,但可以对同一个元素进行多次访问。
- 双向迭代器(Bidirectional Iterators):双向迭代器可以在容器中向前和向后移动。它扩展了前向迭代器的功能,使得迭代器可以使用
--
、++
操作符向前后移动。双向迭代器在像list
、set
和map
这样的容器中非常有用。不能使用类似it + n
的表达式来直接跳转到容器中的第n
个元素。 - 随机访问迭代器(Random Access Iterators):这是最强大的迭代器类型,它支持所有前面提到的迭代器的功能,并且能够进行随机访问。这意味着除了能够向前和向后移动,随机访问迭代器还能够直接跳跃到任意位置(如通过
+
或-
操作符)。vector
和deque
容器提供了随机访问迭代器。
3、示例
使用迭代器遍历并修改
std::vector
中元素,示例:
#include <iostream>
#include <vector>int main() {std::vector<int> v = {1, 2, 3, 4, 5};// 使用迭代器遍历并修改 vector 中的元素for (std::vector<int>::iterator it = v.begin(); it != v.end(); ++it) {*it *= 2; // 将每个元素的值乘以 2}// 打印修改后的 vectorfor (int val : v) {std::cout << val << " ";}std::cout << std::endl;return 0;
}
在这个例子中,我们创建了一个std::vector<int>
并初始化了它。然后,我们使用迭代器遍历了这个vector
,并通过解引用迭代器来修改每个元素的值。最后,我们打印了修改后的vector
。
4、迭代器失效
迭代器失效是STL编程中常见的问题。当容器发生变化时(如元素被删除、容器被重新分配或插入新元素等),之前获取的迭代器可能会失效,即不再指向有效的元素或不再有意义。为了避免迭代器失效导致的未定义行为,应该谨慎操作容器,并在必要时更新迭代器。
4.1、vector
迭代器失效分析
std::vector
是C++标准库中的一个序列容器,它提供了动态数组的功能。然而,在使用std::vector
的迭代器时,需要注意迭代器失效的问题。迭代器失效通常发生在容器的底层存储结构发生改变时,如扩容、插入或删除元素等。
迭代器失效的原因:
- 扩容操作: 当
std::vector
需要增加其容量以容纳更多元素时,它可能会分配一个新的更大的内存块,并将现有元素复制到新的内存块中。这个过程中,原有的内存块会被释放,因此指向原有内存块的迭代器将失效。引起扩容的操作包括:resize()、insert()、push_back()
等。 - 插入操作: 在
std::vector
中插入元素时,如果插入点之后的元素需要移动位置以腾出空间给新元素,那么插入点之后的迭代器将失效。这是因为这些迭代器现在指向的是已经被移动的元素的新位置,而不再是它们原来的位置。 - 删除操作: 使用
erase()
删除std::vector
中的元素时,删除点之后的元素会向前移动以填补被删除元素留下的空隙。虽然这不会改变底层存储结构(即不会重新分配内存),但删除点之后的迭代器仍然会失效,因为它们现在指向的是已经被移动的元素的新位置。
如何避免迭代器失效:
在进行插入、删除或扩容操作后,如果需要继续使用迭代器,应该重新获取迭代器。例如,在使用
insert()
或erase()
后,可以使用它们返回的迭代器(如果适用)或重新从容器的begin()
或end()
开始遍历。
示例:
#include <iostream>
#include <vector> int main() { std::vector<int> vec = {1, 2, 3, 4, 5}; // 使用迭代器遍历vector并删除元素 for (auto it = vec.begin(); it != vec.end(); /* 注意这里没有递增迭代器的操作 */) { if (*it % 2 == 0) { // 假设我们要删除所有偶数元素 // 使用erase删除当前元素,并获取新的迭代器 it = vec.erase(it); // erase返回删除点之后的迭代器 // 注意:此时it已经更新为指向下一个元素的迭代器,因此不需要再递增 } else { // 如果当前元素不是偶数,则递增迭代器以继续遍历 ++it; } } // 输出剩余的元素 for (int num : vec) { std::cout << num << " "; } std::cout << std::endl; return 0;
}
4.2、list
迭代器失效分析
std::list
的迭代器在大多数情况下不会因为容器的扩容或元素移动而失效。然而,std::list
的迭代器在某些操作下仍然可能失效。
迭代器失效的原因:
- 元素删除: 当使用
erase()
删除 std::list 中的元素时,指向被删除元素的迭代器会失效。这是因为erase()
操作会移除链表中的节点,而迭代器实际上是指向这些节点的指针或引用。 - 迭代器自身失效: 如果迭代器被显式地销毁或赋值为其他迭代器的值(特别是如果赋值操作导致了迭代器的复制或移动,并且原始迭代器被销毁),那么该迭代器将失效。
如何避免迭代器失效:
当使用
erase()
删除元素时,应该立即使用erase()
返回的迭代器(它指向被删除元素之后的第一个元素)来更新你的迭代器。
示例:
#include <iostream>
#include <list> int main() { std::list<int> lst = {1, 2, 3, 4, 5}; // 使用迭代器遍历list并删除元素 for (auto it = lst.begin(); it != lst.end(); /* 注意这里没有固定的递增操作位置 */) { if (*it % 2 == 0) { // 假设我们要删除所有偶数元素 it = lst.erase(it); // 使用erase删除当前元素,并更新迭代器 // 注意:此时it已经更新为指向下一个元素的迭代器(如果存在) } else { // 如果当前元素不是偶数,则递增迭代器以继续遍历 ++it; } } // 输出剩余的元素 for (int num : lst) { std::cout << num << " "; } std::cout << std::endl; return 0;
}
4.3、set
与 map
迭代器失效分析
std::map
和std::set
是两种常见的关联容器。它们基于平衡二叉树(通常是红黑树)实现,提供了高效的查找、插入和删除操作。插入新元素时不会导致已有的迭代器失效,删除元素时,只会让当前的迭代器失效,别的迭代器不受影响。但是,在遍历容器的过程如果调用erase()
函数删除元素,需要更新迭代器。当删除元素时,下面两种方法都可有效避免迭代器失效:
方法一:使用erase()
返回值更新迭代器
set<int> valset = { 1,2,3,4,5,6 };
set<int>::iterator iter;
for (iter = valset.begin(); iter != valset.end(); ){if (3 == *iter){iter = valset.erase(iter);} else {++iter;}
}
方法二:使用it ++
set<int> valset = { 1,2,3,4,5,6 };
set<int>::iterator iter;
for (iter = valset.begin(); iter != valset.end(); ){if (3 == *iter){valset.erase(iter++);} else {++iter;}
}
传给
erase()
的是iter的一个副本,iter++是下一个有效的迭代器。
5、总结
STL迭代器是C++标准模板库中的一个重要概念,它提供了一种统一的方法来访问和遍历容器中的元素。通过迭代器可以实现复杂的算法和容器操作,而无需关心容器的具体类型。了解迭代器的类型和使用方法对于编写高效、可维护的C++代码至关重要。
相关文章:
C++ STL(1)迭代器
文章目录 一、迭代器详解1、迭代器的定义与功能2、迭代器类型3、示例4、迭代器失效4.1、vector 迭代器失效分析4.2、list 迭代器失效分析4.3、set 与 map 迭代器失效分析 5、总结 前言: 在C标准模板库(STL)中,迭代器是一个核心概念…...
uview表单校验不生效问题
最近几次使用发现有时候会不生效,具体还没排查出来什么原因,先记录一下解决使用方法 <u--formlabelPosition"top"labelWidth"auto":model"form":rules"rules"ref"uForm" ><view class"…...
前端开发设计模式——单例模式
目录 一、单例模式的定义和特点: 1.定义: 2.特点: 二、单例模式的实现方式: 1.立即执行函数结合闭包实现: 2.ES6类实现: 三、单例模式的应用场景 1.全局状态管理: 2.日志记录器: …...
行情叠加量化,占据市场先机!
A股久违的3000点,最近都没有更新,现在终于对我们的市场又来点信息。相信在座的朋友这几天都是喜笑颜开,对A股又充满信心。当前行情好起来了,很多朋友又开始重回市场,研究股票学习量化,今天我们给大家重温下…...
大厂面试真题-ConcurrentHashMap怎么保证的线程安全?
ConcurrentHashMap是Java中的一个线程安全的哈希表实现,它通过一系列精妙的机制来保证线程安全。以下是ConcurrentHashMap保证线程安全的主要方式: 分段锁(Segment Locking,Java 1.8之前): 在Java 1.8之前的…...
【RabbitMQ】消息堆积、推拉模式
消息堆积 原因 消息堆积是指在消息队列中,待处理的消息数量超过了消费者处理能力,导致消息在队列中不断堆积的现象。通常有以下几种原因: 消息生产过快:在高流量或者高负载的情况下,生产者以极高的速率发送消息&…...
MySQL常用SQL语句(持续更新中)
文章目录 数据库相关表相关索引相关添加索引 编码相关系统变量相关 收录一些经常用到的sql 数据库相关 建数据库 CREATE DATABASE [IF NOT EXISTS] <数据库名> [[DEFAULT] CHARACTER SET <字符集名>] [[DEFAULT] COLLATE <校对规则名>];例如: C…...

【更新】红色文化之红色博物馆数据集(经纬度+地址)
数据简介:红色博物馆作为国家红色文化传承与爱国主义教育的重要基地,遍布全国各地,承载着丰富的革命历史与文化记忆。本数据说明旨在汇总并分析全国范围内具有代表性的红色博物馆的基本信息,包括其地址、特色及教育意义࿰…...

Python项目Flask框架整合Redis
一、在配置文件中创建Redis连接信息 二、 实现Redis配置类 import redis from config.config import REDIS_HOST, REDIS_PORT, REDIS_PASSWD, REDIS_DB, EXPIRE_TIMEclass RedisDb():def __init__(self, REDIS_HOST, REDIS_PORT, REDIS_DB, EXPIRE_TIME, REDIS_PASSWD):# 建立…...

完整网络模型训练(一)
文章目录 一、网络模型的搭建二、网络模型正确性检验三、创建网络函数 一、网络模型的搭建 以CIFAR10数据集作为训练例子 准备数据集: #因为CIFAR10是属于PRL的数据集,所以需要转化成tensor数据集 train_data torchvision.datasets.CIFAR10(root&quo…...

高效便捷,体验不一样的韩语翻译神器
嘿,大家好啊!今天想跟大家聊聊我用过的几款翻译神器,特别是它们在翻译韩语时的那些小感受。作为一个偶尔需要啃啃韩语资料或者跟韩国朋友聊天的普通人,我真心觉得这些翻译工具简直就是我的救星! 一、福昕在线翻译 网址…...

Markdown笔记管理工具Haptic
什么是 Haptic ? Haptic 是一个新的本地优先、注重隐私的开源 Markdown 笔记管理工具。它简约、轻量、高效,旨在提供您所需的一切,而不包含多余的功能。 目前官方提供了 docker 和 Mac 客户端。 Haptic 仍在积极开发中。以下是未来计划的一些…...

网络原理-传输层UDP
上集回顾: 上一篇博客中讲述了应用层如何自定义协议:确定传输信息,确定数据格式 应用层也有一些现成的协议:HTTP协议 这一篇博客中来讲述传输层协议 传输层 socket api都是传输层协议提供的(操作系统内核实现的了…...

C++中,如何使你设计的迭代器被标准算法库所支持。
iterator(读写迭代器) const_iterator(只读迭代器) reverse_iterator(反向读写迭代器) const_reverse_iterator(反向只读迭代器) 以经常介绍的_DList类为例,它的迭代…...
Java NIO 全面详解:掌握 `Path` 和 `Files` 的一切
在 Java 7 中引入的 NIO (New I/O) 为文件系统和流的操作带来了强大的能力,其中 Path 和 Files 是核心部分。Path 作为对文件路径的抽象,提供了灵活的方式处理文件系统中的路径;Files 则通过一系列静态方法,使得文件的读写、复制、…...
bluez免提协议hands-free介绍,全到无法想象,bluez hfp ag介绍
零. 前言 由于Bluez的介绍文档有限,以及对Linux 系统/驱动概念、D-Bus 通信和蓝牙协议都有要求,加上网络上其实没有一个完整的介绍Bluez系列的文档,所以不管是蓝牙初学者还是蓝牙从业人员,都有不小的难度,学习曲线也相对较陡,所以我有了这个想法,专门对Bluez做一个系统…...

关于区块链的安全和隐私
背景 区块链技术在近年来发展迅速,被认为是安全计算的突破,但其安全和隐私问题在不同应用中的部署仍处于争论焦点。 目的 对区块链的安全和隐私进行全面综述,帮助读者深入了解区块链的相关概念、属性、技术和系统。 结构 首先介绍区块链…...

特征工程——一门提高机器学习性能的艺术
当前围绕人工智能(AI)和机器学习(ML)展开的许多讨论以模型为中心,聚焦于 ML和深度学习(DL)的最新进展。这种模型优先的方法往往对用于训练这些模型的数据关注不足,甚至完全忽视。类似MLOps的领域正迅速发展,通过系统性地训练和利用ML模型&…...

Paper解读:工作场所人机协作的团队形成:促进组织变革的目标编程模型
人工智能(AI)具有降低运营成本、提高效率和改善客户体验的潜力。 因此,在组织中组建项目团队至关重要,这样他们就会在决策过程中欢迎人工智能。 当前的技术革命要求公司快速变革,并增加了对团队在促进创新采用方面的作…...

图文深入理解Oracle Network配置管理(一)
List item 本篇图文深入介绍Oracle Network配置管理。 Oracle Network概述 Oracle Net 服务 Oracle Net 监听程序 <oracle_home>/network/admin/listener.ora <oracle_home>/network/admin/sqlnet.ora建立网络连接 要建立客户机或中间层连接,Oracle…...
基于算法竞赛的c++编程(28)结构体的进阶应用
结构体的嵌套与复杂数据组织 在C中,结构体可以嵌套使用,形成更复杂的数据结构。例如,可以通过嵌套结构体描述多层级数据关系: struct Address {string city;string street;int zipCode; };struct Employee {string name;int id;…...
浅谈 React Hooks
React Hooks 是 React 16.8 引入的一组 API,用于在函数组件中使用 state 和其他 React 特性(例如生命周期方法、context 等)。Hooks 通过简洁的函数接口,解决了状态与 UI 的高度解耦,通过函数式编程范式实现更灵活 Rea…...

对WWDC 2025 Keynote 内容的预测
借助我们以往对苹果公司发展路径的深入研究经验,以及大语言模型的分析能力,我们系统梳理了多年来苹果 WWDC 主题演讲的规律。在 WWDC 2025 即将揭幕之际,我们让 ChatGPT 对今年的 Keynote 内容进行了一个初步预测,聊作存档。等到明…...
Java入门学习详细版(一)
大家好,Java 学习是一个系统学习的过程,核心原则就是“理论 实践 坚持”,并且需循序渐进,不可过于着急,本篇文章推出的这份详细入门学习资料将带大家从零基础开始,逐步掌握 Java 的核心概念和编程技能。 …...
Fabric V2.5 通用溯源系统——增加图片上传与下载功能
fabric-trace项目在发布一年后,部署量已突破1000次,为支持更多场景,现新增支持图片信息上链,本文对图片上传、下载功能代码进行梳理,包含智能合约、后端、前端部分。 一、智能合约修改 为了增加图片信息上链溯源,需要对底层数据结构进行修改,在此对智能合约中的农产品数…...

技术栈RabbitMq的介绍和使用
目录 1. 什么是消息队列?2. 消息队列的优点3. RabbitMQ 消息队列概述4. RabbitMQ 安装5. Exchange 四种类型5.1 direct 精准匹配5.2 fanout 广播5.3 topic 正则匹配 6. RabbitMQ 队列模式6.1 简单队列模式6.2 工作队列模式6.3 发布/订阅模式6.4 路由模式6.5 主题模式…...
绕过 Xcode?使用 Appuploader和主流工具实现 iOS 上架自动化
iOS 应用的发布流程一直是开发链路中最“苹果味”的环节:强依赖 Xcode、必须使用 macOS、各种证书和描述文件配置……对很多跨平台开发者来说,这一套流程并不友好。 特别是当你的项目主要在 Windows 或 Linux 下开发(例如 Flutter、React Na…...

6.9-QT模拟计算器
源码: 头文件: widget.h #ifndef WIDGET_H #define WIDGET_H#include <QWidget> #include <QMouseEvent>QT_BEGIN_NAMESPACE namespace Ui { class Widget; } QT_END_NAMESPACEclass Widget : public QWidget {Q_OBJECTpublic:Widget(QWidget *parent nullptr);…...

向量几何的二元性:叉乘模长与内积投影的深层联系
在数学与物理的空间世界中,向量运算构成了理解几何结构的基石。叉乘(外积)与点积(内积)作为向量代数的两大支柱,表面上呈现出截然不同的几何意义与代数形式,却在深层次上揭示了向量间相互作用的…...
如何通过git命令查看项目连接的仓库地址?
要通过 Git 命令查看项目连接的仓库地址,您可以使用以下几种方法: 1. 查看所有远程仓库地址 使用 git remote -v 命令,它会显示项目中配置的所有远程仓库及其对应的 URL: git remote -v输出示例: origin https://…...