关于stl容器的迭代器失效问题

2023-10-27

场景

  在项目中使用stl容器的时候,多线程环境下出错,调试很久发现问题是使用容器的时候由于容器扩容导致的线程不安全,还有扩容导致的迭代器失效问题,于是就想着把迭代器失效的问题总结一下。

场景重现1
  我在项目开发中使用vector时,由于扩容导致的迭代器失效。

#include <iostream>
#include <vector>
#include <list>
#include <deque>
#include <map>
#include <unordered_map>
#include <thread>
#include <algorithm>
using namespace std;

int main()
{
    vector<int> v;
    v.push_back(1);
    auto iter = find(v.begin(), v.end(), 1);  //iter指向的是数组中第一个元素
    cout << "iter所指元素的值:" << *iter << endl;
    cout << "容器容量capacity:" << v.capacity() << endl << endl;

    for (int i = 0; i < 10; i++)
    {
        v.push_back(666);
    }
    
    cout << "iter所指元素的值:" << *iter << endl;
    cout << "容器容量capacity:" << v.capacity() << endl;
    return 0;
}

运行结果:可以看到,iter迭代器一开始指向的元素是1,但是由于vector底层扩容,导致元素从一个空间迁移到另一个空间,原来的空间释放,iter指向了已经释放的内存(悬挂指针),从而导致结果出错。
在这里插入图片描述


场景重现2
  在我另一个项目里面使用到了unordered_map,但是和前面的迭代器失效不一样,这里是一个线程在执行find函数的时候,另一个线程插入元素,导致哈希表扩容rehash,从而原来应该在哈希表中的数据find不到出现错误。

  调用find函数的流程:哈希表每个元素可以看做是一个桶,下满挂着的是链表(或者是红黑树),调用find的时候首先会通过哈希映射找到对应的桶,然后在桶下面去遍历找对应的键,如果找不到就会返回umap.end();

#include <iostream>
#include <vector>
#include <list>
#include <deque>
#include <map>
#include <unordered_map>
#include <thread>
#include <algorithm>
using namespace std;

int main()
{
    // unordered_map线程不安全
    unordered_map<int, int> umap;
    umap[3] = 1;
    thread t1([&]()
    {
        for (int i = 0; i < 100000; i++)
        {
            auto iter = umap.find(3);
            iter->second = 3;
        } 
    });
    t1.detach();

    thread t2([&]()
    {
        for (int i = 10; i < 100000; i++)
        {
            umap[i] = i;
        } 
    });
    t2.detach();

    return 0;
}

  运行结果:可能会运行成功,但是有时候也会报段错误。使用gdb调试可以看到,在通过iter访问值的时候,会报错,但是3在umap中是一定存在的,我们一开始就已经初始化了键为3的值。
  原因:当一个线程在调用find函数的时候,通过哈希映射找到了桶的指针,此时另一个线程往umap中插入元素导致哈希表扩容rehash,每个桶指针从原来的内存迁移到另一个内存空间中,此时find继续往后找就会找到nullptr,于是就返回umap.end(),里面是一个空指针,通过空指针去访问变量自然会报段错误。

在这里插入图片描述


其他

   vector底层是连续的内存空间,除了扩容导致的迭代器失效,即使没有发生扩容,插入元素也会导致后面的迭代器失效,因为插入位置之后的元素都会向后移动,使得原来的迭代器不再指向原来的元素。删除也是同理,也会造成插入位置之后的迭代器都失效,同时删除位置的迭代器也会失效。
   list底层是不连续的内存空间,所以插入元素,扩容都不会导致迭代器失效,但是删除元素会导致指向删除元素的迭代器失效。

如何解决删除的时候迭代器失效的问题:

    for (auto iter = v.begin(); iter != v.end();)
    {
        if (*iter % 2 == 0)
        {
            iter = v.erase(iter);
        }
        else
        {
            ++iter;
        }
    }

  可以使用如上所示的方法,删除数组中所有的偶数,删除后erase会返回下一个元素的迭代器,用它重新赋值迭代器即可。

测试程序

#include <iostream>
#include <vector>
#include <list>
#include <deque>
#include <map>
#include <unordered_map>
#include <thread>
#include <algorithm>
using namespace std;

int main()
{
    vector<int> v = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
    for (auto num : v)
    {
        cout << num << endl;
    }
    for (auto iter = v.begin(); iter != v.end();)
    {
        if (*iter % 2 == 0)
        {
            iter = v.erase(iter);
        }
        else
        {
            ++iter;
        }
    }
    cout << "***" << endl;
    for (auto num : v)
    {
        cout << num << endl;
    }

    return 0;
}

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

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

关于stl容器的迭代器失效问题 的相关文章

  • 注销租约抛出 InvalidOperationException

    我有一个使用插件的应用程序 我在另一个应用程序域中加载插件 我使用 RemoteHandle 类http www pocketsilicon com post Things That Make My Life Hell Part 1 App
  • Directory.Delete 之后 Directory.Exists 有时返回 true ?

    我有非常奇怪的行为 我有 Directory Delete tempFolder true if Directory Exists tempFolder 有时 Directory Exists 返回 true 为什么 可能是资源管理器打开了
  • 如何让 Swagger 插件在自托管服务堆栈中工作

    我已经用 github 上提供的示例重新提出了这个问题 并为任何想要自己运行代码的人提供了一个下拉框下载链接 Swagger 无法在自托管 ServiceStack 服务上工作 https stackoverflow com questio
  • 在 C 中匹配二进制模式

    我目前正在开发一个 C 程序 需要解析一些定制的数据结构 幸运的是我知道它们是如何构造的 但是我不确定如何在 C 中实现我的解析器 每个结构的长度都是 32 位 并且每个结构都可以通过其二进制签名来识别 举个例子 有两个我感兴趣的特定结构
  • 为什么 Google 测试会出现段错误?

    我是 Google Test 的新手 正在尝试提供的示例 我的问题是 当我引入失败并设置GTEST BREAK ON FAILURE 1 或使用命令行选项 GTest 将出现段错误 我正在考虑这个例子 https code google c
  • 回发后刷新时提示确认表单重新提交。我做错了什么?

    我有一个以空白 默认状态启动的仪表板 我让用户能够将保存的状态加载到仪表板中 当他们单击 应用 按钮时 我运行以下代码 function CloseAndSave var radUpload find radUpload1ID var in
  • qdbusxml2cpp 未知类型

    在使用 qdbusxml2cpp 程序将以下 xml 转换为 Qt 类时 我收到此错误 qdbusxml2cpp c ObjectManager a ObjectManager ObjectManager cpp xml object ma
  • 为什么调用非 const 成员函数而不是 const 成员函数?

    为了我的目的 我尝试包装一些类似于 Qt 共享数据指针的东西 经过测试 我发现当应该调用 const 函数时 会选择它的非 const 版本 我正在使用 C 0x 选项进行编译 这是一个最小的代码 struct Data int x con
  • 是否有实用的理由使用“if (0 == p)”而不是“if (!p)”?

    我倾向于使用逻辑非运算符来编写 if 语句 if p some code 我周围的一些人倾向于使用显式比较 因此代码如下所示 if FOO p some code 其中 FOO 是其中之一false FALSE 0 0 0 NULL etc
  • 使用自定义堆的类似 malloc 的函数

    如果我希望使用自定义预分配堆构造类似 malloc 的功能 那么 C 中最好的方法是什么 我的具体问题是 我有一个可映射 类似内存 的设备 已将其放入我的地址空间中 但我需要获得一种更灵活的方式来使用该内存来存储将随着时间的推移分配和释放的
  • C#:帮助理解 UML 类图中的 <>

    我目前正在做一个项目 我们必须从 UML 图编写代码 我了解 UML 类图的剖析 但我无法理解什么 lt
  • Azure 辅助角色“请求输入之一超出范围”的内部异常。

    我在辅助角色中调用 CloudTableClient CreateTableIfNotExist 方法 但收到一个异常 其中包含 请求输入之一超出范围 的内部异常 我做了一些研究 发现这是由于将表命名为非法表名引起的 但是 我尝试为我的表命
  • 如何禁用 fread() 中的缓冲?

    我正在使用 fread 和 fwrite 读取和写入套接字 我相信这些函数用于缓冲输入和输出 有什么方法可以在仍然使用这些功能的同时禁用缓冲吗 Edit 我正在构建一个远程桌面应用程序 远程客户端似乎 落后于服务器 我不知道可能是什么原因
  • 为什么 std::strstream 被弃用?

    我最近发现std strstream已被弃用 取而代之的是std stringstream 我已经有一段时间没有使用它了 但它做了我当时需要做的事情 所以很惊讶听到它的弃用 我的问题是为什么做出这个决定 有什么好处std stringstr
  • Cmake 链接共享库:包含库中的头文件时“没有这样的文件或目录”

    我正在学习使用 CMake 构建库 构建库的代码结构如下 include Test hpp ITest hpp interface src Test cpp ITest cpp 在 CMakeLists txt 中 我用来构建库的句子是 f
  • 如何在非控制台应用程序中查看 cout 输出?

    输出到调试窗口似乎相当繁琐 我在哪里可以找到cout如果我正在编写非控制台信息 则输出 Like double i a b cout lt lt b lt lt endl I want to check out whether b is z
  • 如何部署“SQL Server Express + EF”应用程序

    这是我第一次部署使用 SQL Server Express 数据库的应用程序 我首先使用实体 框架模型来联系数据库 我使用 Install Shield 创建了一个安装向导来安装应用程序 这些是我在目标计算机中安装应用程序所执行的步骤 安装
  • WebSocket安全连接自签名证书

    目标是一个与用户电脑上安装的 C 应用程序交换信息的 Web 应用程序 客户端应用程序是 websocket 服务器 浏览器是 websocket 客户端 最后 用户浏览器中的 websocket 客户端通过 Angular 持久创建 并且
  • 我的班级应该订阅自己的公共活动吗?

    我正在使用 C 3 0 遵循标准事件模式我有 public event EventHandler
  • 使用 .NET Process.Start 运行时挂起进程 - 出了什么问题?

    我在 svn exe 周围编写了一个快速而肮脏的包装器来检索一些内容并对其执行某些操作 但对于某些输入 它偶尔会重复挂起并且无法完成 例如 一个调用是 svn list svn list http myserver 84 svn Docum

随机推荐

  • JAVA 敏感词过滤

    package me mymilkbottles import org apache commons lang CharUtils import java io File import java util HashMap import ja
  • 基于vue+leaflet+echart的足迹分享评论平台

    其实题目是随便取的 目的只是用来证明Vue leaflet springboot技术栈的可行性 效果 小专栏不支持上传视频 想看的话可以去我的知乎看最新的文章 那个应该可以 在这里 主要功能描述 vue leaflet结合 足迹管理 新建
  • python编程-2.编写程序,输出所有由1,2,3,4这四个数字组成的素数,并且每个数字在素数中只出现一次。

    data用于存储在一定范围内的素数 data set for n in range 1234 4321 1 if n 2 0 continue for i in range 3 int n 0 5 1 2 if n i 0 break el
  • 组合逻辑电路——编码器

    组合逻辑电路 编码器 概念 编码的概念 在数字系统中 常需要将有特定意义的信息编成二进制代码 这一过程称为编码 编码器 实现编码的数字电路被称为编码器 二进制编码器 这里我们采用与非门来设计二进制编码器 二进制编码器输出端数量不定 可以根据
  • ACM MM 2022

    有预训练 460多m 来源丨https zhuanlan zhihu com p 547671620 Bidirectional Self Training with Multiple Anisotropic Prototypes for
  • Glide使用及原理分析

    文章目录 前言 一 Glide的基本使用 二 Glide的网络请求 1 HttpURLConnection实现一个原生图片加载框架 2 Glide为什么能监听网络变化 三 Glide的生命周期 1 Fragment的生命周期 动态加载Fra
  • 解决线程安全问题的三种方法

    解决线程安全问题的三种方法 一 使用同步代码块 如 卖票案例 出现了线程安全 重复的票不能出现 步骤 成员位置建立锁对象 synchronized 锁对象 出现安全问题代码 1 锁对象 任意对象 2 必须保证多个线程使用的是同一个锁对象 3
  • pip配置问题解决-如何使用修改windows系统环境变量

    问题发现 在使用pip安装环境的时候 出现了如下的问题 解决办法 我是在windows系统环境变量上添加上python的Scripts文件夹路径 将其放到环境变量的path中去 操作如下 右键我的电脑 点开属性 在最下面的 关于 上 找到
  • 力扣:删除链表中的节点

    237 删除链表中的节点 请编写一个函数 用于 删除单链表中某个特定节点 在设计函数时需要注意 你无法访问链表的头节点 head 只能直接访问 要被删除的节点 题目数据保证需要删除的节点 不是末尾节点 示例 1 输入 head 4 5 1
  • 区块链节点和区块区别_区块链的常识之,区块链节点,是什么?

    专业科普 区块链节点 通常指的是区块链网络中的计算机 也就是说任何连接到区块链网络的计算机 包括手机 矿机等 都称为节点 比如说比特币网络 是一个公有链 用户在自己的联网电脑上运行比特币程序时 这个电脑就成为比特币区块链网络中的一个节点 是
  • 利用栈实现简单表达式求值

    简单表达式求值 关键点 首先明确要使用的数据结构 本文采用栈来实现 为了分别操作数字和运算符 采用双栈 一个数值栈和一个运算符栈 根据栈顶运算符和待入栈运算符的优先级的判断 产生中间结果 而中间结果作为最终结果的一部分需要再次入栈 栈顶运算
  • DEDECMS单独调用指定文章

    dede arclist idlist 指定ID limit 0 1 a href field title a 描述 field description dede arclist
  • js中获取body html元素

  • myBatis实现多对多操作的sql语句

    文章目录 1 角色对人 2 人对角色 3 创建数据库语句 总结 1 角色对人 实现角色对人的多对多查询 将有角色的人筛选出来 实现角色对人的多对多查询 SELECT u r id AS rid r role name r role desc
  • Go_方法、方法重写、方法与函数的区别

    方法 方法是绑定在自定义类型上的 常用在结构体上 方法方法不能直接调用 只能通过所绑定s类型的变量来调用 因为方法是和类型做关联的 方法是值拷贝的传递方式 如果希望改变结构体变量的值 需要通过结构体指针实现 方法名首字母大写为公共 小写为私
  • Tomcat的下载及其使用

    目录 一 Tomcat是什么 二 Tomcat的下载安装 1 在搜索框搜索Tomcat 2 下载 3 Tomcat里面的一些具体内容 三 运行Tomcat 1 直接点击脚本运行 2 使用浏览器访问 3 部署页面到Tomcat 一 Tomca
  • Win10如何彻底删除360的办法

    很多用户在购买电脑或者重装系统之后都会给电脑安装360安全卫士 其实360是一款知名的流氓软件 感觉进行了彻底的删除工作 其实还残留了很多 那Win10如何彻底删除360呢 下面小编就来给大家展示一下具体的办法 2022新版Win10 64
  • SQL Part3 --- 聚合操作符

    SQL 聚合操作符 聚合操作符 Aggregate Operators COUNT A SUM A AVG A MAX A MIN A GROUP BY and HAVING 聚合操作符 Aggregate Operators Sailor
  • 在Spring-Boot中进行单元测试

    要进行单元测试 需要引入依赖
  • 关于stl容器的迭代器失效问题

    场景 在项目中使用stl容器的时候 多线程环境下出错 调试很久发现问题是使用容器的时候由于容器扩容导致的线程不安全 还有扩容导致的迭代器失效问题 于是就想着把迭代器失效的问题总结一下 场景重现1 我在项目开发中使用vector时 由于扩容导