[C++] STL_vector 迭代器失效问题

在这里插入图片描述

文章目录

  • 1、前言
  • 2、情况一:底层空间改变的操作
  • 3、情况二:指定位置元素的删除操作
  • 4、g++编译器对迭代器失效检测
    • 4.1 扩容
    • 4.2 erase删除任意位置(非尾删)
    • 4.3 erase尾删
  • 5、总结

1、前言

**迭代器的主要作用就是让算法能够不用关心底层数据结构,其底层实际就是一个指针,或者是对指针进行了封装,比如:string的迭代器就是原生指针char,vector的迭代器就是原生态指针T因此迭代器失效,实际就是迭代器底层对应指针所指向的空间被销毁了,而使用一块已经被释放的空间,造成的后果是程序崩溃(即如果继续使用已经失效的迭代器,程序可能会崩溃)。
对迭代器失效我们了解了,那么现在我们就分析,在vector中哪些操作会导致迭代器失效。

2、情况一:底层空间改变的操作

存在底层空间改变的函数接口有:resize、reserve、insert、assign、push_back等。
产生的原因:
这几个接口都存在扩容的问题,扩容的时候存在异地扩容,当异地扩容后,原本的空间被释放,但是迭代器指的是被释放空间,这就会导致迭代器的失效问题,会引发程序崩溃的问题。
解决方法:
一旦存在扩容,扩容后对迭代器更新一次,重新给迭代器赋值即可。
举例:
我们看一下insert接口。
在这里插入图片描述

我们由图中可以看到,当我们需要在3之前插入数据30,但是空间已经满了,因此我们需要进行扩容,扩容是异地开空间,开好空间将旧空间的数据拷贝回来,并将旧空间释放掉,_start指向新的空间头部,但是it指的是旧空间的位置,这就是迭代器失效。我们记住it相对于_start的相对位置,在新空间开好后,更新it,让其指向新空间的相对位置。(方式:计算出it到_start的距离len,开好新空间后,更新it为新的_start+len)。
代码实现:

iterator insert(iterator pos, const T& x)
{assert(pos >= _start);assert(pos <= _finish);if (_finish == _endOfStorage){size_t len = pos - _start;//先记下_start到pos位置的距离,因为扩容后迭代器pos就会失效reserve(capacity() == 0 ? 4 : 2 * capacity());pos = _start + len;//新的空间需要更新迭代器pos}iterator end = _finish - 1;//挪动数据while (end >= pos){*(end + 1) = *end;--end;}*pos = x;++_finish;return pos;
}

3、情况二:指定位置元素的删除操作

对于erase接口也会导致迭代器失效问题。那它是怎么导致的呢,我们来分析一下。
产生原因:
在erase删除pos位置元素后,pos位置之后的元素会往前搬移,没有导致底层空间的改变,理论上讲迭代器不应该会失效,但是:如果pos刚好是最后一个元素,删完之后pos刚好是end的位置,而end位置是没有元素的,那么pos就失效了。因此删除vector中任意位置上元素时,vs就认为该位置迭代器失效了。
在这里插入图片描述

#include <iostream>
using namespace std;
#include <vector>int main()
{int a[] = { 1, 2, 3, 4 };vector<int> v(a, a + sizeof(a) / sizeof(int));// 使用find查找3所在位置的iteratorvector<int>::iterator pos = find(v.begin(), v.end(), 3);// 删除pos位置的数据,导致pos迭代器失效。v.erase(pos);cout << *pos << endl; // 此处会导致非法访问return 0;
}

解决方法:
本质是因为尾删导致的迭代器失效问题,因此我们在尾删完后,返回it的下一个位置,我们的模拟实现是数据覆盖(it+1覆盖it),因此返回的还是it,一删之后 --_finish,当 it指的位置就是_finish 的时候正好也就停止了,因此也就解决了迭代器失效的问题。
代码实现:

iterator erase(iterator pos)
{assert(pos >= _start);assert(pos < _finish);iterator it = pos + 1;//挪动数据while (it < _endOfStorage){*(it - 1) = *it;++it;}--_finish;return pos;
}

4、g++编译器对迭代器失效检测

Linux下,g++编译器对迭代器失效的检测并不是非常严格,处理也没有vs2019下极端。
我们来看下面这几种情况下,代码在vs2019和g++下不同的表现。

4.1 扩容

int main()
{vector<int> v{1,2,3,4,5};for(size_t i = 0; i < v.size(); ++i)cout << v[i] << " ";cout << endl;auto it = v.begin();cout << "扩容之前,vector的容量为: " << v.capacity() << endl;v.reserve(100);cout << "扩容之后,vector的容量为: " << v.capacity() << endl;while(it != v.end()){cout << *it << " ";++it;}cout << endl;return 0;
}

g++下运行结果:
在这里插入图片描述

vs2019下运行结果:
在这里插入图片描述

vs2019下程序崩溃了。
结论:当扩容后迭代器就是失效的,g++下虽然能运行,但是结果出错了,vs下直接程序崩溃。

4.2 erase删除任意位置(非尾删)

#include <vector>
#include <algorithm>
using namespace std;int main()
{vector<int> v{1,2,3,4,5};vector<int>::iterator it = find(v.begin(), v.end(), 3);v.erase(it);cout << *it << endl;while(it != v.end()){cout << *it << " ";++it;}cout << endl;return 0;
}

g++下运行结果:
在这里插入图片描述

vs2019下运行结果:
在这里插入图片描述

结论:在非尾删的删除中,空间是没有变的,迭代器指的是还是那块空间,g++下迭代器没有失效,删除后后面的数据前移,it位置没失效,vs下只要是erase,就判断为迭代器失效了。

4.3 erase尾删

int main()
{vector<int> v{1,2,3,4,5,6};auto it = v.begin();while (it != v.end()){if (*it % 2 == 0)v.erase(it);++it;}for (auto e : v)cout << e << " ";cout << endl;return 0;
}

g++下运行结果:
在这里插入图片描述

vs2019下不用看,直接崩溃。
结论:当在尾删的时候,删除之后存在数据挪动,一挪动_finish与it是一个位置了,erase本就返回被删除位置的下一个位置,此时迭代器失效,再++it程序直接崩溃。

5、总结

本篇主要讲了扩容、插入、删除造成的迭代器失效,g++对迭代器失效检测的不严格,而vs对迭代器失效检测很严格,直接崩溃。
1、扩容一般都要更新迭代器,我们不知道哪一次的扩容是异地扩。
2、插入任意位置时,一旦存在扩容就要更新迭代器,本质就是扩容要更新迭代器。
3、删除任意位置时,g++下非尾删不考虑迭代器失效问题,尾删一定要注意迭代器失效问题;vs2019中删除就认定为迭代器失效,直接崩溃。

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

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

相关文章

DataWhale 机器学习夏令营第三期——任务二:可视化分析

DataWhale 机器学习夏令营第三期 学习记录二 (2023.08.23)——可视化分析1.赛题理解2. 数据可视化分析2.1 用户维度特征分布分析2.2 时间特征分布分析 DataWhale 机器学习夏令营第三期 ——用户新增预测挑战赛 学习记录二 (2023.08.23)——可视化分析 2023.08.17 已跑通baseli…

Android沉浸式实现(记录)

沉浸式先看效果 直接上代码 Android manifest文件 android:theme"style/Theme.AppCompat.NoActionBar"布局文件 <?xml version"1.0" encoding"utf-8"?> <androidx.constraintlayout.widget.ConstraintLayout xmlns:android"ht…

mit s0681 lab2 Trace系统调用实现

实验一 实现一个用户级别的程序&#xff0c;功能为&#xff0c;指定系统调用后&#xff0c;跟踪程序的系统调用情况 分析实验 实验目标为实现一个程序去跟踪指定程序的系统调用。因此目标有两个 实现一个程序跟踪目标程序的系统调用 实现1&#xff0c;就需要在用户这边实…

4.18 TCP 和 UDP 可以使用同一个端口吗?

目录 TCP 和 UDP 可以同时绑定相同的端口吗&#xff1f; 多个 TCP 服务进程可以绑定同一个端口吗&#xff1f; 重启 TCP 服务进程时&#xff0c;为什么会有“Address in use”的报错信息&#xff1f; 重启 TCP 服务进程时&#xff0c;如何避免“Address in use”的报错信息…

HarmonyOS/OpenHarmony应用开发-ArkTS语言渲染控制LazyForEach数据懒加载

LazyForEach从提供的数据源中按需迭代数据&#xff0c;并在每次迭代过程中创建相应的组件。当LazyForEach在滚动容器中使用了&#xff0c;框架会根据滚动容器可视区域按需创建组件&#xff0c;当组件划出可视区域外时&#xff0c;框架会进行组件销毁回收以降低内存占用。一、接…

智驾算力芯片市场仍处于「波动」周期,Momenta曝光自研NPU

用「冷热不均」来形容当下的汽车芯片赛道&#xff0c;再合适不过了。 本周&#xff0c;英伟达公布的第二财季&#xff08;5-7月&#xff09;营收达到创纪录的135亿美元&#xff0c;大幅超出了此前市场普遍预期的略高于110亿美元&#xff0c;同比增速更是达到了101%。 其中&…

接口测试总结分享(http与rpc)

接口测试是测试系统组件间接口的一种测试。接口测试主要用于检测外部系统与系统之间以及内部各个子系统之间的交互点。测试的重点是要检查数据的交换&#xff0c;传递和控制管理过程&#xff0c;以及系统间的相互逻辑依赖关系等。 一、了解一下HTTP与RPC 1. HTTP&#xff08;H…

【python】输出高亮信息的内容

背景 日志是定位问题和数据分析的关键手段之一&#xff0c;尤其是在调试阶段&#xff0c;高效的、具有辨识度的日志可以非常快速准确的进行问题定位。shell中的echo命令自带文本格式化输出的功能&#xff0c;我们先来回顾下基本的语法&#xff0c;然后套用到python中即可。 s…

Docker安装及Docker构建简易版Hadoop生态

一、首先在VM创建一个新的虚拟机将Docker安装好 更新系统&#xff1a;首先打开终端&#xff0c;更新系统包列表。 sudo apt-get update sudo apt-get upgrade下图是更新系统包截图 安装Docker&#xff1a;使用以下命令在Linux上安装Docker。 sudo apt-get install -y docker.i…

pytestx容器化执行引擎

系统架构 前端、后端、pytest均以Docker容器运行服务&#xff0c;单独的容器化执行引擎&#xff0c;项目环境隔离&#xff0c;即用即取&#xff0c;用完即齐&#xff0c;简单&#xff0c;高效。 前端容器&#xff1a;页面交互&#xff0c;请求后端&#xff0c;展示HTML报告 后…

华为云渲染实践

// 编者按&#xff1a;云计算与网络基础设施发展为云端渲染提供了更好的发展机会&#xff0c;华为云随之长期在自研图形渲染引擎、工业领域渲染和AI加速渲染三大方向进行云渲染方面的探索与研究。本次LiveVideoStackCon 2023上海站邀请了来自华为云的陈普&#xff0c;为大家分…

【ag-grid-vue】column

网格中的每一列都使用列定义(ColDef)来定义。列根据在网格选项中指定的列定义的顺序在网格中定位。 列定义 下面的例子展示了一个定义了3列的简单网格: <template><ag-grid-vuestyle"height: 300px; width: 1000px"class"ag-theme-balham":colum…

自动化的驱动力,工控机助您实现智能生产!

“智能工厂建设如火如荼&#xff0c;部分成果已经落地&#xff0c;在大规模资金投入的市场催化下&#xff0c;海尔、海信等制造企业通过智能工厂手段推进生产效率成倍增长的新闻层出不穷。在工业4.0时代&#xff0c;“中国制造2025”战略中&#xff0c;智能工厂构建都是其中不可…

【KMP算法-代码随想录】

目录 1.什么是KMP2.什么是next数组3.什么是前缀表&#xff08;1&#xff09;前后缀含义&#xff08;2&#xff09;最长公共前后缀&#xff08;3&#xff09;前缀表的必要性 4.计算前缀表5.前缀表与next数组&#xff08;1&#xff09;使用next数组来匹配 6.构造next数组&#xf…

1.linux的常用命令

目录 一、Linux入门 二、Linux文件系统目录 三、Linux的vi和vim的使用 四、Linux的关机、重启、注销 四、Linux的用户管理 五、Linux的运行级别 六、Linux的文件目录指令 七、Linux的时间日期指令 八、Linux的压缩和解压类指令 九、Linux的搜索查找指令 ​​​​​​…

windows可视化界面管理服务器上的env文件

需求&#xff1a;在 Windows 环境中通过可视化界面编辑位于 Linux 主机上的 env 文件的情况&#xff0c;我现在环境是windows环境&#xff0c;我的env文件在linux的192.168.20.124上&#xff0c;用户是op&#xff0c;密码是op&#xff0c;文件绝对路径是/home/op/compose/env …

无涯教程-PHP - 性能优化

根据Zend小组的说明,以下插图显示了PHP 7与PHP 5.6和基于流行的基于PHP的应用程序上的HHVM 3.7。 Magento 1.9 与执行Magento事务的PHP 5.6相比&#xff0c;PHP 7的运行速度证明是其两倍。 Drupal 7 在执行Drupal事务时&#xff0c;与PHP 5.6相比&#xff0c;PHP 7的运行速度…

SQL 大小敏感问题

在SQL中&#xff0c;关键字和函数名 是不区分 大小写的 比如&#xff08;select、where、order by 、group by update 等关键字&#xff09;&#xff0c;以及函数(ABS、MOD、round、min等) window系统默认是大小写不敏感 &#xff08;ZEN文件和zen 文件 不能同时存在&#xff…

【vue3+ts项目】配置husky+配置commitlint

上一篇文章中配置了eslint校验代码工具 【vue3ts项目】配置eslint校验代码工具&#xff0c;eslintprettierstylelint 1、配置husky 每次手动执行命令才能格式化代码&#xff0c;如果有人没有格式化就提交到远程仓库&#xff0c;这个规范就起不到作用了&#xff0c;所有需要强…

Arcgis colorRmap

arcgis中colorRmap对应的名称&#xff1a; 信息来源&#xff1a;https://developers.arcgis.com/documentation/common-data-types/raster-function-objects.htm 在arcpy中使用方法&#xff1a; import arcpy cr arcpy.mp.ColorRamp("Yellow to Red")python中 ma…