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…...

大话软工笔记—需求分析概述
需求分析,就是要对需求调研收集到的资料信息逐个地进行拆分、研究,从大量的不确定“需求”中确定出哪些需求最终要转换为确定的“功能需求”。 需求分析的作用非常重要,后续设计的依据主要来自于需求分析的成果,包括: 项目的目的…...
多场景 OkHttpClient 管理器 - Android 网络通信解决方案
下面是一个完整的 Android 实现,展示如何创建和管理多个 OkHttpClient 实例,分别用于长连接、普通 HTTP 请求和文件下载场景。 <?xml version"1.0" encoding"utf-8"?> <LinearLayout xmlns:android"http://schemas…...

如何在看板中体现优先级变化
在看板中有效体现优先级变化的关键措施包括:采用颜色或标签标识优先级、设置任务排序规则、使用独立的优先级列或泳道、结合自动化规则同步优先级变化、建立定期的优先级审查流程。其中,设置任务排序规则尤其重要,因为它让看板视觉上直观地体…...
条件运算符
C中的三目运算符(也称条件运算符,英文:ternary operator)是一种简洁的条件选择语句,语法如下: 条件表达式 ? 表达式1 : 表达式2• 如果“条件表达式”为true,则整个表达式的结果为“表达式1”…...
【算法训练营Day07】字符串part1
文章目录 反转字符串反转字符串II替换数字 反转字符串 题目链接:344. 反转字符串 双指针法,两个指针的元素直接调转即可 class Solution {public void reverseString(char[] s) {int head 0;int end s.length - 1;while(head < end) {char temp …...

2025 后端自学UNIAPP【项目实战:旅游项目】6、我的收藏页面
代码框架视图 1、先添加一个获取收藏景点的列表请求 【在文件my_api.js文件中添加】 // 引入公共的请求封装 import http from ./my_http.js// 登录接口(适配服务端返回 Token) export const login async (code, avatar) > {const res await http…...

Module Federation 和 Native Federation 的比较
前言 Module Federation 是 Webpack 5 引入的微前端架构方案,允许不同独立构建的应用在运行时动态共享模块。 Native Federation 是 Angular 官方基于 Module Federation 理念实现的专为 Angular 优化的微前端方案。 概念解析 Module Federation (模块联邦) Modul…...

微服务商城-商品微服务
数据表 CREATE TABLE product (id bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT 商品id,cateid smallint(6) UNSIGNED NOT NULL DEFAULT 0 COMMENT 类别Id,name varchar(100) NOT NULL DEFAULT COMMENT 商品名称,subtitle varchar(200) NOT NULL DEFAULT COMMENT 商…...

uniapp微信小程序视频实时流+pc端预览方案
方案类型技术实现是否免费优点缺点适用场景延迟范围开发复杂度WebSocket图片帧定时拍照Base64传输✅ 完全免费无需服务器 纯前端实现高延迟高流量 帧率极低个人demo测试 超低频监控500ms-2s⭐⭐RTMP推流TRTC/即构SDK推流❌ 付费方案 (部分有免费额度&#x…...
unix/linux,sudo,其发展历程详细时间线、由来、历史背景
sudo 的诞生和演化,本身就是一部 Unix/Linux 系统管理哲学变迁的微缩史。来,让我们拨开时间的迷雾,一同探寻 sudo 那波澜壮阔(也颇为实用主义)的发展历程。 历史背景:su的时代与困境 ( 20 世纪 70 年代 - 80 年代初) 在 sudo 出现之前,Unix 系统管理员和需要特权操作的…...