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…...
uniapp 对接腾讯云IM群组成员管理(增删改查)
UniApp 实战:腾讯云IM群组成员管理(增删改查) 一、前言 在社交类App开发中,群组成员管理是核心功能之一。本文将基于UniApp框架,结合腾讯云IM SDK,详细讲解如何实现群组成员的增删改查全流程。 权限校验…...
云原生核心技术 (7/12): K8s 核心概念白话解读(上):Pod 和 Deployment 究竟是什么?
大家好,欢迎来到《云原生核心技术》系列的第七篇! 在上一篇,我们成功地使用 Minikube 或 kind 在自己的电脑上搭建起了一个迷你但功能完备的 Kubernetes 集群。现在,我们就像一个拥有了一块崭新数字土地的农场主,是时…...
(十)学生端搭建
本次旨在将之前的已完成的部分功能进行拼装到学生端,同时完善学生端的构建。本次工作主要包括: 1.学生端整体界面布局 2.模拟考场与部分个人画像流程的串联 3.整体学生端逻辑 一、学生端 在主界面可以选择自己的用户角色 选择学生则进入学生登录界面…...
MFC内存泄露
1、泄露代码示例 void X::SetApplicationBtn() {CMFCRibbonApplicationButton* pBtn GetApplicationButton();// 获取 Ribbon Bar 指针// 创建自定义按钮CCustomRibbonAppButton* pCustomButton new CCustomRibbonAppButton();pCustomButton->SetImage(IDB_BITMAP_Jdp26)…...
【SpringBoot】100、SpringBoot中使用自定义注解+AOP实现参数自动解密
在实际项目中,用户注册、登录、修改密码等操作,都涉及到参数传输安全问题。所以我们需要在前端对账户、密码等敏感信息加密传输,在后端接收到数据后能自动解密。 1、引入依赖 <dependency><groupId>org.springframework.boot</groupId><artifactId...
WEB3全栈开发——面试专业技能点P2智能合约开发(Solidity)
一、Solidity合约开发 下面是 Solidity 合约开发 的概念、代码示例及讲解,适合用作学习或写简历项目背景说明。 🧠 一、概念简介:Solidity 合约开发 Solidity 是一种专门为 以太坊(Ethereum)平台编写智能合约的高级编…...
Webpack性能优化:构建速度与体积优化策略
一、构建速度优化 1、升级Webpack和Node.js 优化效果:Webpack 4比Webpack 3构建时间降低60%-98%。原因: V8引擎优化(for of替代forEach、Map/Set替代Object)。默认使用更快的md4哈希算法。AST直接从Loa…...
基于Java+VUE+MariaDB实现(Web)仿小米商城
仿小米商城 环境安装 nodejs maven JDK11 运行 mvn clean install -DskipTestscd adminmvn spring-boot:runcd ../webmvn spring-boot:runcd ../xiaomi-store-admin-vuenpm installnpm run servecd ../xiaomi-store-vuenpm installnpm run serve 注意:运行前…...
【堆垛策略】设计方法
堆垛策略的设计是积木堆叠系统的核心,直接影响堆叠的稳定性、效率和容错能力。以下是分层次的堆垛策略设计方法,涵盖基础规则、优化算法和容错机制: 1. 基础堆垛规则 (1) 物理稳定性优先 重心原则: 大尺寸/重量积木在下…...
【java】【服务器】线程上下文丢失 是指什么
目录 ■前言 ■正文开始 线程上下文的核心组成部分 为什么会出现上下文丢失? 直观示例说明 为什么上下文如此重要? 解决上下文丢失的关键 总结 ■如果我想在servlet中使用线程,代码应该如何实现 推荐方案:使用 ManagedE…...
