C++ STL vector list set map容器循环通过迭代器删除元素注意事项

news2024/11/18 11:20:55

      先说说写这篇博客的原因吧,同事转部门了,把他手头的工作交接给了我。他以前维护的一个模块,会将外部输入的数据缓存起来分段处理,处理完了就会清除缓存数据,最近出现了一个bug,缓存数据一直不清除,反复处理同样的一批数据,导致该处理的数据得不到处理,引起业务的中断。经过仔细分析代码,发现其清理策略存在缺陷,我就将清理策略进行了调整,程序能够执行到一个清理函数,结果在清除过程中出现了崩溃,经过分析发现是用迭代器清除list容器中的元素后迭代器没有更新,下一次继续使用这个无效的迭代器进行操作,从而引发崩溃,汗颜啊,代码大致逻辑如下:

using request_tuple = std::tuple<long, int>;
std::list<request_tuple>    m_que;

void OperList() {
	for (int i = 0; i < 10; i++) {
		m_que.push_back(request_tuple(i, i));//(std::make_tuple(i, i));
	}

	std::ostringstream os;
	char sp = '\t';
	std::for_each(m_que.begin(), m_que.end(), [&os, sp](request_tuple item){
		os << std::get<0>(item) << ' ' << std::get<1>(item) << sp;
	});
	std::cout << os.str() << std::endl;
	
	auto iter = m_que.begin();
	for (; iter != m_que.end();) {
		if (0 == std::get<0>(*iter) % 2) {
			m_que.erase(iter);
		}
		else {
			iter++;
		}
	}
	os.clear();
	os.str("");
	std::for_each(m_que.begin(), m_que.end(), [&os, sp](request_tuple item){
		os << std::get<0>(item) << ' ' << std::get<1>(item) << sp;
	});
	std::cout << os.str() << std::endl;
}

出问题的代码是 m_que.erase(iter);,m_que是一个list容器,用迭代器删除容器中满足条件的元素,删除后iter已经变成无效,但是没有更新其值,下一次循环使用iter时引起了崩溃。

介于此,本人就讲常用的vector,list,set,map容器通过迭代器删除元素的正确用法予以总结。

目录

1. vector

2. list

3. set

4. map


关于C++容器,详细可参考:容器库 - C++中文 - API参考文档

1. vector

template<

    class T,
    class Allocator = std::allocator<T>

> class vector;

   vector是表示可以改变大小的数组的序列容器。就像数组一样,vector使用连续存储空间存储元素,这意味着它们的元素也可以使用指向其元素的指针进行偏移来访问,并与数组一样高效。但与数组不同的是, vector的大小可以动态变化,并且是由容器自动处理的。与其他动态序列容器(deques,lists和forward_lists)相比,vector可以非常高效地访问其元素(就像数组一样)并且相对高效地从其末尾添加或删除元素。 对于涉及在末尾以外的位置插入或删除元素的操作,性能比其他序列容器要差,并且与lists和forward_lists相比具有更少的迭代器和引用一致性与其他动态序列容器(deques,lists和forward_lists)相比,vector可以非常高效地访问其元素(就像数组一样)并且相对高效地从其末尾添加或删除元素。 对于涉及在末尾以外的位置插入或删除元素的操作,性能比其他序列容器要差,并且与lists和forward_lists相比具有更少的迭代器和引用一致性。迭代器删除方法如下:

std::vector<int> vec;
auto iter = vec.begin();
iter = vec.erase(iter);

2. list

template<

    class T,
    class Allocator = std::allocator<T>

> class list;

       list是一种序列容器,它允许在序列中的任意位置进行常数时间的插入和删除操作,并可以在两个方向上进行迭代(遍历)。list容器是基于双链表实现的,可以将其包含的每个元素存储在不同且不相关的存储位置上。通过链接到前一个元素和后一个元素的每个元素的关联关系在链表内部保持顺序。迭代器删除方法如下:

std::list<int> li;
auto iter = li.begin();
// list方法一删除

iter = li.erase(iter);
// list方法二删除
li.erase(iter++);

3. set

template<

    class Key,
    class Compare = std::less<Key>,
    class Allocator = std::allocator<Key>

> class set;

std::set 是关联容器,含有 Key 类型对象的已排序集。用比较函数 比较 (Compare) 进行排序。搜索、移除和插入拥有对数复杂度。 set 通常以红黑树实现。迭代器删除方法如下:

std::set<int> si;
auto iter = si.begin();
// set方法一删除
iter = si.erase(iter);

// set方法二删除
si.erase(iter++);

4. map

template<

    class Key,
    class T,
    class Compare = std::less<Key>,
    class Allocator = std::allocator<std::pair<const Key, T> >

> class map;

std::map 是有序键值对容器,它的元素的键是唯一的。用比较函数 Compare 排序键。搜索、移除和插入操作拥有对数复杂度。 map 通常实现为红黑树。std::map 是有序键值对容器,它的元素的键是唯一的。用比较函数 Compare 排序键。搜索、移除和插入操作拥有对数复杂度。 map 通常实现为红黑树。迭代器删除方法如下:

std::map<int, bool> mi;
auto iter = mi.begin();
// map方法一删除
iter = mi.erase(iter);

// map方法二删除
mi.erase(iter++);

完整代码如下:
 

// UseStl.cpp : 定义控制台应用程序的入口点。
//

#include "stdafx.h"
#include <iostream>
#include <sstream>
#include <vector>
#include <list>
#include <set>
#include <map>
#include <tuple>
#include <algorithm>

using namespace std;

using request_tuple = std::tuple<long, int>;
std::list<request_tuple>    m_que;

void ErrorOperList() {
	for (int i = 0; i < 10; i++) {
		m_que.push_back(request_tuple(i, i));//(std::make_tuple(i, i));
	}

	std::ostringstream os;
	char sp = '\t';
	std::for_each(m_que.begin(), m_que.end(), [&os, sp](request_tuple item){
		os << std::get<0>(item) << ' ' << std::get<1>(item) << sp;
	});
	std::cout << os.str() << std::endl;
	
	auto iter = m_que.begin();
	for (; iter != m_que.end();) {
		if (0 == std::get<0>(*iter) % 2) {
			m_que.erase(iter);
		}
		else {
			iter++;
		}
	}
	os.clear();
	os.str("");
	std::for_each(m_que.begin(), m_que.end(), [&os, sp](request_tuple item){
		os << std::get<0>(item) << ' ' << std::get<1>(item) << sp;
	});
	std::cout << os.str() << std::endl;
}


void OperVector() {
	int arr[15] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 15, 14, 13, 12, 11};
	std::vector<int> vec;
	for (int i = 0; i < sizeof(arr) / sizeof(int); i++) {
		vec.push_back(arr[i]);
	}
	std::for_each(vec.begin(), vec.end(), [](const int& num){
		std::cout << num << ' ';
	});
	std::cout << endl;

	auto iter = vec.begin();
	for (; iter != vec.end();) {
		if (0 == *iter % 2) {
			std::cout << *iter << std::endl;
			iter = vec.erase(iter);
		}
		else {
			iter++;
		}
	}
	std::for_each(vec.begin(), vec.end(), [](const int& num){
		std::cout << num << ' ';
	});
	std::cout << endl;
}

void OperList() {
	int arr[15] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 15, 14, 13, 12, 11 };
	std::list<int> li;
	for (int i = 0; i < sizeof(arr) / sizeof(int); i++) {
		li.push_back(arr[i]);
	}
	std::for_each(li.begin(), li.end(), [](const int& num){
		std::cout << num << ' ';
	});
	std::cout << endl;

	bool erase_type = true;
	auto iter = li.begin();
	for (; iter != li.end();) {
		if (0 == *iter % 2) {
			if (erase_type) {
				std::cout << "erase_type true ::" << *iter << std::endl;
				// list方法一删除
				iter = li.erase(iter);
				erase_type = false;
			}
			else {
				std::cout << "erase_type false ::" << *iter << std::endl;
				// list方法二删除
				li.erase(iter++);
				erase_type = true;
			}
		}
		else {
			iter++;
		}
	}
	std::for_each(li.begin(), li.end(), [](const int& num){
		std::cout << num << ' ';
	});
	std::cout << endl;
}

void OperSet() {
	int arr[15] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 15, 14, 13, 12, 11 };
	std::set<int> si;
	for (int i = 0; i < sizeof(arr) / sizeof(int); i++) {
		si.insert(arr[i]);
	}
	std::for_each(si.begin(), si.end(), [](const int& num){
		std::cout << num << ' ';
	});
	std::cout << endl;

	bool erase_type = true;
	auto iter = si.begin();
	for (; iter != si.end();) {
		if (0 == *iter % 2) {
			if (erase_type) {
				std::cout << "erase_type true ::" << *iter << std::endl;
				// set方法一删除
				iter = si.erase(iter);
				erase_type = false;
			}
			else {
				std::cout << "erase_type false ::" << *iter << std::endl;
				// set方法二删除
				si.erase(iter++);
				erase_type = true;
			}
		}
		else {
			iter++;
		}
	}
	std::for_each(si.begin(), si.end(), [](const int& num){
		std::cout << num << ' ';
	});
	std::cout << endl;
}

void OperMap() {
	int arr[15] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 15, 14, 13, 12, 11 };
	std::map<int, bool> mi;
	for (int i = 0; i < sizeof(arr) / sizeof(int); i++) {
		mi.insert(std::pair<int, bool>(arr[i], true));
	}
	std::for_each(mi.begin(), mi.end(), [](const std::pair<int, bool>& item){
		std::cout << item.first << ' ';
	});
	std::cout << endl;

	bool erase_type = true;
	auto iter = mi.begin();
	for (; iter != mi.end();) {
		if (0 == iter->first % 2) {
			if (erase_type) {
				std::cout << "erase_type true ::" << iter->first << std::endl;
				// map方法一删除
				iter = mi.erase(iter);
				erase_type = false;
			}
			else {
				std::cout << "erase_type false ::" << iter->first << std::endl;
				// map方法二删除
				mi.erase(iter++);
				erase_type = true;
			}
		}
		else {
			iter++;
		}
	}

	std::for_each(mi.begin(), mi.end(), [](const std::pair<int, bool>& item){
		std::cout << item.first << ' ';
	});
	std::cout << endl;
}

int _tmain(int argc, _TCHAR* argv[])
{
	std::cout << __cplusplus << std::endl;
	//std::cout << argc << " " << argv[0] << std::endl;
	std::cout << "迭代器删除vector中元素" << std::endl;
	OperVector();
	std::cout << "迭代器删除list中元素" << std::endl;
	OperList();
	std::cout << "迭代器删除set中元素" << std::endl;
	OperSet();
	std::cout << "迭代器删除map中元素" << std::endl;
	OperMap();

	return 0;
}

运行结果如下:

总结:通过以上可以看出vector,list,set,map容器均可以通过如下方式借助迭代器删除元素。

iter = container.erase(iter);

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

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

相关文章

【SpringMVC】非注解的处理器映射器和适配器

项目目录 1.导入的依赖 pom.xml <properties><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding><maven.compiler.source>1.8</maven.compiler.source><maven.compiler.target>1.8</maven.compiler.target><…

【K3s】第2篇 一篇文章学习实践K3s部署安装

目录 1、docker安装 2、docker-compose安装 3、K3s安装 3.1 k3s与install.sh文件准备 3.2 k3s 安装步骤 4、查看k3s部署状态 1、docker安装 方式一 https://fanjufei.blog.csdn.net/article/details/123500511https://fanjufei.blog.csdn.net/article/details/123500511 …

12.24

接口测试 ​ <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta http-equiv"X-UA-Compatible" content"IEedge"><meta name"viewport" content"widthdevice-width…

Unity3D异步加载场景SceneManager.LoadSceneAsync()卡住,并不异步,获取process直接到0.9的问题

问题阐述&#xff1a; 一般来说&#xff0c;在加载场景的时候&#xff0c;会因为所加载资源的大小、复杂度、电脑配置等因素导致加载过程耗费一定的时间。虽然这个加载时间是不可避免的&#xff0c;但是在这一小段卡着的时间里如果就这样卡着的话会大大降低玩家体验。 所以很多…

(matlab编程基础)数组的基本操作

目录 1、数组寻址 2、数组元素删除 3、数组查找和排序 &#xff08;1&#xff09;数组查找 &#xff08;2&#xff09;数组排序 4、数组运算 5、数组操作函数 数组操作主要从以下5部分进行介绍:数组寻址、数组元素的删除、数组查找和排序、数组运算和数组操作函数。 1、…

PS CS6视频剪辑基本技巧(五)添加logo、动画和画中画

系列讲座导读 PS CS6视频剪辑基本技巧&#xff08;一&#xff09;CS6可以实现的视频剪辑功能 PS CS6视频剪辑基本技巧&#xff08;二&#xff09;视频剪接和添加图片 PS CS6视频剪辑基本技巧&#xff08;三&#xff09;添加声音和字幕 PS CS6视频剪辑基本技巧&#xff08;四&am…

RMQ - ST表

RMQ - ST表 1、RMQ 简介 RMQ (Range Minimum / Maximum Query) 问题是指&#xff1a;对于长度为 nnn 的数列 AAA&#xff0c;回答若干询问 (A,i,j)(A, i, j)(A,i,j) (1≤i,j≤n)(1≤i,j≤n)(1≤i,j≤n)&#xff0c;返回数列 A 中区间在 [i,j][i,j][i,j] 中的最小 (大) 值所在…

Vue2.0全面教程

Vue2.0 学习视频地址 文章目录Vue2.01.vue 简介1.1.什么是vue1.2.vue的两个特性1.2.1.数据驱动视图1.2.2.双向数据绑定1.3.MVVM概述1.4.MVVM工作原理2.vue的基本使用2.1.基本使用步骤2.2.基本代码与MVVM的对应关系3.vue的调试工具3.1.安装vue-devtool调试工具3.2.配置Chrome浏…

Axios(三)

目录 1.Axios的默认配置 2.Axios创建Ajax实例对象发送请求 3.Axios拦截器 4.Axios取消请求 5.Axios文件结构说明 6.Axios创建过程 7.Axios对象创建过程模拟实现 8.Axios发送请求过程详解 9.模拟实现Axios发送请求 1.Axios的默认配置 <!doctype html> <html …

QT系列第7节 标准对话框使用

QT编程中对话框作为最常用的窗口经常被使用&#xff0c;本节介绍一些QT常用的标准对话框使用。 目录 1.选择单文件 2.选择多文件 3.选择目录 4.文件存储 5.选择颜色 6.选择字体 7.输入字符换/整数/浮点数/条目 8.消息对话框 9.进度对话框 10.向导对话框 1.选择单文件…

【圣诞节限定】今天教你如何用Html+JS+CSS绘制3D动画圣诞树

一、前言 应CSDN的邀请&#xff0c;这次给大家展示一波&#xff0c;如何用H5技术绘制3D圣诞树。 二、创意名 只采用简单的HtmlJSCSS 技术绘制。 三、效果展示 圣诞树修过如下&#xff1a; 四、编码实现 将源码复制保存到html中打开即可。 <!DOCTYPE html> <html lang…

ChatGPT与BimAnt的1小时对话实录【数字孪生】

本文为BimAnt和ChatGPT对数字孪生相关问题的解答&#xff0c;感觉这个AI真的已经“懂”了很多东西&#xff0c;让人恍惚间忘了是在和bot对话。 BimAnt&#xff1a;hello ChatGPT&#xff1a;Hello! How can I help you today? BimAnt&#xff1a;can you speak chinese&am…

鲸鱼优化算法及其在无线网络资源分配中的应用(Matlab代码实现)

目录 &#x1f4a5;1 概述 &#x1f4da;2 运行结果 &#x1f389;3 参考文献 &#x1f468;‍&#x1f4bb;4 Matlab代码 &#x1f4a5;1 概述 鲸鱼优化算法&#xff08;Whale Optimization Algorithm&#xff09;是一种新兴的智能优化算法&#xff0c;在2016年提出。算法…

JWT渗透与攻防(二)

目录 前言 JWT漏洞演示之CTFhub&#xff08;一&#xff09; JWT漏洞演示之CTFhub&#xff08;二&#xff09; 前言 我们在之前的文章中已经讲解过了JWT漏洞相关的原理和利用&#xff0c;今天我们就通过这篇文章再来了解一下JWT的漏洞。 JWT漏洞演示之CTFhub&#xff08;一&…

Linux-信号

文章目录信号准备知识&#xff1a;信号产生的方式实验验证&#xff1a;9号信号是不可被捕捉&#xff08;自定义的&#xff09;信号处理&#xff1a;信号产生前&#xff1a;信号产生的方式&#xff1a;键盘实验显示&#xff1a;段错误&#xff08;野指针&#xff09;实验验证&am…

SSRF ME XCTF

题目 就是一个验证框和URL框&#xff0c;两个都必须有参数 解法 验证码 做一个粗略的脚本&#xff0c;一般验证码都是数字&#xff0c;所以直接开md5&#xff1a; def cmpcapt(substr):for i in range(1,100001):md5 hashlib.md5(str(i).encode(utf-8))hexmd5 md5.hexd…

机器学习任务功略

目录 机器学习的结构机器学习攻略 训练集loss较大 model bias问题optimization问题 gradient descent的问题解决 如何区分训练集loss大是model bias还是优化器的问题 测试集loss较大 overfitting过拟合 为什么会有overfitting解决过拟合的方法 训练集与测试集的不匹配 如何选择…

Linux内核基础篇——常用调试技巧汇总

文章目录printk动态输出BUG()和BUG_ON()dump_stack()devmemprintk printk共有8个等级&#xff0c;从0-7&#xff0c;等级依次降低。 打印等级可以通过修改/proc/sys/kernel/printk来改变。 查看printk等级&#xff1a; cat /proc/sys/kernel/printk 7 4 1 7打开内核所有打印…

2022圣诞树(C语言摇钱树版本)

逐梦编程&#xff0c;让中华屹立世界之巅。 简单的事情重复做,重复的事情用心做,用心的事情坚持做&#xff1b; 文章目录前言一、个人感悟二、圣诞树由来三、圣诞树发展历史演变四、常见的圣诞树种类五、摇钱圣诞树效果展示六、实现思路七、编码实现总结新壁纸前言 时光飞逝&a…

前端工程师必须掌握—《Webpack教程》

Webpack 学习视频地址 文章目录Webpack1.webpack基础1.1.初始化隔行变色的案例1.2.安装和配置webpack1.2.1.安装webpack1.2.2.配置webpack1.2.3.了解mode可选值的应用场景1.2.4.自定义打包的入口和出口2.插件2.1.安装和配置webpack-dev-server2.1.1.安装webpack-dev-server2.1…