C++ STL - vector/list讲解及迭代器失效

vector 使用

vector 是一个动态数组.

构造/拷贝构造/赋值重载函数

int main()
{// 是一个模板, 在实例化的时候, 需要指明类型std::vector<int> first; // 一个空的数组std::vector<int> second (4,100); // 设置初始空间大小为 4 个int, 全部初始化为 100std::vector<int> third (second.begin(),second.end()); // 通过迭代器构造std::vector<int> fourth (third); // 直接复制 third, 拷贝构造 std::vector<int> fifth;fifth = fourth;  // 复制重载return 0;
}

空间函数

1. size/capacity

size: 返回 vector 当前存储的元素数量
capacity: 返回 vector 最大存储元素数量

size_type size() const noexcept;
size_type capacity() const noexcept;int main()
{vector<int> v1(10, 1);cout << v1.size() << " " << v1.capacity() << endl;return 0;
}

2. resize/reserve

resize: 修改 vector 对象的 size

reserve: 修改 vector 的容量 capacity

void resize (size_type n);
void resize (size_type n, const value_type& val);void reserve (size_type n);int main()
{vector<int> v1;v1.resize(10, 0);// n < size 则数据丢失, n > capacity 则发生扩容, 用 val 填充扩容的空间v1.reserve(20);// 如果当前容量小于 n,vector 会重新分配内存以增加容量。// 如果当前容量已经大于或等于 n,则 reserve() 不会改变容量。return 0;
}

 reserve修改的只是capacity, 而不是size, 这是由区别的.

int main()
{vector<int> v1;v1.reserve(10);v1[1] = 10; // 这是错误的, reserve(10) 只是开辟了 10 个空间, 这 10 个空间还没有给我们使用v1.resize(10);v1[1] = 10; // 设置size = 10, 让v1分配了 10 个空间使用权给我们
}

3. empty

empty: 检查 vector 是否为空, 为空返回 true, 否者返回 false

bool empty() const noexcept;int main()
{vector<int> v1;if(v1.empty){cout << "数组为空" << endl;}else{cout << "数组不为空" << endl;}
}

访问 

1. operator[] / at

这两个函数都是用于访问 vector 中指定索引的元素

int main()
{vector<int> v1(10);for(int i = 0; i < 10; i++){v1[i] = i;}v1.at(5) = 50;cout << v1[2] << endl;cour << v1.at(5) << endl;
}

区别: operator[] 不会进行边界检查, 如果访问位置超出范围, 行为是未定义的

         at 则会进行边界检查, 访问位置超出范围, 则抛出异常

2. front / back

front: 返回数组头部的元素

back: 返回数组末尾的那个元素

int main()
{vector<int> v1(10);for(int i = 0; i < 10; i++){v1[i] = i;}cout << v1.front() << endl;cout << v1.back() << endl;return 0;
}

3. data

data: 返回一个指向 vector 内部数组的指针, 允许直接访问底层数组

int main()
{vector<int> v1(10);for(int i = 0; i < 10; i++){v1[i] = i;}int* arr = v1.data();cour << arr[5] << endl;return 0;
}

修改

1. push_back / pop_back

push_back: 向 vector 的末尾插入一个元素

pop_back: 删除 vector 末尾的一个元素

int main()
{vector<int> v1;v1.push_back(2);cout << v1return 0;
}

2. insert / erase

insert: 向指定的位置 (位置是一个迭代器) 后面插入一个元素, 并返回指向插入元素的迭代器

erase: 删除指定位置的元素, 返回删除位置的后一个元素的迭代器

#include <iostream>
#include <vector>int main ()
{std::vector<int> myvector (3,100);std::vector<int>::iterator it;it = myvector.begin();it = myvector.insert ( it , 200 );cout << *it << endl;it = myvector.erase(it);std::cout << *it << std::endl;return 0;
}

3. swap / clear

swap: 交换两个 vector 的值

clear: 清空 vector 中的元素

int main()
{vector<int> v1(3, 200);vector<int> v2(5, 100);v1.swap(v2);cout << v1[1] << endl;v1.clear();if(v1.empty()){cout << "v1为空" << endl;}return 0;
}

list 使用

list 底层是一个双向链表, 链表擅长的就是插入删除操作.

构造list

int main()
{std::list<int> first;                                // empty list of intsstd::list<int> second (4,100);                       // four ints with value 100std::list<int> third (second.begin(),second.end());  // iterating through secondstd::list<int> fourth (third);return 0;
}

容量

empty / size

和上面 vector 中的函数作用相同
STL 容器中的函数名称非常相似, 功能也相似, 熟悉完 vector 之后, list 使用也很简单

empty: 判断 list 是否为空

size: 返回链表的长度.

int main()
{list<int> l1(3, 10);cout << "l1 的长度为: " << l1.size() << endl;if(l1.empty()){cout << "l1 为空" << endl;}return 0;
}

修改

list 提供了非常多的插入删除操作, 因为相比于 vector, 双向链表插入删除效率更高

push_back / push_front / pop_back / pop_front

push_back: 在 list 尾部插入一个元素

push_front: 在 list 头部插入一个元素

pop_back: 在 list 尾部删除一个元素

pop_front: 在 list 头部删除一个元素

int main()
{list<int> l1(2, 10);l1.push_back(20);l1.push_frong(5);// 在 string 中说过, 迭代器的使用方法和指针差不多std::list<int>::iterator it = l1.begin(); // 获取 l1 首元素的迭代器while(it != l2.end()){cout << *it << " ";}l1.pop_back();l1.pop_front();while(it != l2.end()){cout << *it << " ";}return 0;
}

insert / erase

insert: 向指定的位置 (位置是一个迭代器) 的前面插入一个元素, 并返回指向插入元素的迭代器

erase: 删除指定位置的元素, 返回删除位置的后一个元素的迭代器

int main ()
{std::list<int> mylist;std::list<int>::iterator it;// set some initial values:for (int i=1; i<=5; ++i) mylist.push_back(i); // 1 2 3 4 5it = mylist.begin();++it;       // it points now to number 2           ^mylist.insert (it,10);                        // 1 10 2 3 4 5// "it" still points to number 2                      ^mylist.insert (it,2,20);                      // 1 10 20 20 2 3 4 5mylist.erase(it);it = mylist.begin();while(it != mylist.end()){cout << *it << " ";}
}

迭代器失效问题

迭代器失效: 指的是由于容器(如std::vectorstd::list等)的某些操作导致之前获取的迭代器不再指向容器中的有效元素,或者不再指向任何元素,从而不能被安全使用的现象。

可以理解为迭代器就是指针, 那么指针什么时候会失效: 所指向的空间是错误的, 那么哪些操作会导致指针指向的空间发生变化.

vector 迭代器失效情况

在 vector 中, 有以下情况会导致迭代器失效

  1. 插入操作:当使用 push_backinsert 插入元素时,如果 vector 的容量不足以容纳新元素,它可能会重新分配内存,这会导致所有迭代器失效。即使不重新分配内存,insert 操作也会使插入点之后的迭代器失效。

  2. 删除操作:使用 erasepop_back 删除元素时,指向被删除元素的迭代器会失效,同时,指向被删除元素之后的所有迭代器也会失效。

  3. 容量调整:调用 resizereserve 调整容量时,如果新容量大于当前容量,可能会导致重新分配内存,从而使所有迭代器失效

 再看这段代码

int main()
{vector<int> v1 = {2, 3, 4, 4, 5};std::vector<int>::iterator it = v1.begin();while(it != v1.end()){if(*it % 2 == 0){v1.erase(it);}++it;}return 0;
}

看似每次检查完当前元素之后, it 向后走一步, 检查下一个.
实际上, 因为进行了删除操作, 数据进行了挪动,
导致后面的元素顺序发生变化, it 所指向的内容就变成了未定义行为.

解决方法

int main()
{vector<int> v1 = {2, 3, 4, 4, 5};std::vector<int>::iterator it = v1.begin();while(it != v1.end()){if(*it % 2 == 0){it = v1.erase(it);}}return 0;
}

上面介绍 erase 和 insert 的时候说了: 这两个函数会返回一个迭代器.
此时返回的迭代器是有效的. 我们使用返回的迭代器代替已经失效的迭代器.

list 迭代器失效

list 迭代器失效: 

删除操作:使用 erase 删除元素时,只有指向被删除元素的迭代器会失效。其他迭代器,包括指向被删除元素之前和之后的元素的迭代器,仍然有效。

int main()
{std::list<int> lst = {1, 2, 3, 4, 5};std::list<int>::iterator it = lst.begin(); // it 指向第一个元素lst.erase(it); // 仅 it 失效,lst.begin() 之后的迭代器仍然有效++it; // 未定义行为,it 已经失效// it = lst.erase(it);  应该将 it 进行更新
}

所以当指向了可能导致迭代器失效的操作后, 就不要再使用那个旧的迭代器了.
需要更新当前的迭代器.

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

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

相关文章

AWS 新加坡EC2 VPS 性能、线路评测及免费注意事项

原文论坛给你更好的阅读讨论体验&#x1f490;&#xff1a; AWS 新加坡EC2 VPS 性能、线路评测及免费注意事项 - VPS - 波波论坛 引言 对于那些习惯薅“羊毛”的朋友来说&#xff0c; AWS 的 免费套餐 可能已经非常熟悉。这台vps是我用外币卡薅的免费的12个月的机器&#xf…

TritonServer中加载模型,并在Gunicorn上启动Web服务调用模型

TritonServer中加载模型,并在Gunicorn上启动Web服务调用模型 一、TritonServer中加载模型1.1 搭建本地仓库1.2 配置文件1.3 服务端代码1.4 启动TritonServer二、Gunicorn上启动Web服务2.1 安装和配置Gunicorn2.2 启动Gunicorn三、调用模型四、性能优化与监控五、总结在深度学习…

容器安全检测和渗透测试工具

《Java代码审计》http://mp.weixin.qq.com/s?__bizMzkwNjY1Mzc0Nw&mid2247484219&idx1&sn73564e316a4c9794019f15dd6b3ba9f6&chksmc0e47a67f793f371e9f6a4fbc06e7929cb1480b7320fae34c32563307df3a28aca49d1a4addd&scene21#wechat_redirect Docker-bench-…

使用ENSP实现NAT

一、项目拓扑 二、项目实现 1.路由器AR1配置 进入系统试图 sys将路由器命名为R1 sysname R1关闭信息中心 undo info-center enable进入g0/0/0接口 int g0/0/0将g0/0/0接口IP地址配置为12.12.12.1/30 ip address 12.12.12.1 30进入e0/0/1接口 int g0/0/1将g0/0/1接口IP地址配置…

pnpm:包管理的新星,平替 npm 和 yarn

​ pnpm&#xff0c;一个老牌的 node.js 包管理器&#xff0c;支持 npm 的所有功能&#xff0c;完全足以用来替代 npm。它采用全局存储&#xff0c;每个项目内部使用了硬链接&#xff0c;所以很省空间&#xff0c;安装速度快。 本文介绍下 pnpm 的基本概念&#xff0c;安装、…

【大数据学习 | Spark-Core】Spark的分区器(HashPartitioner和RangePartitioner)

之前学过的kv类型上面的算子 groupby groupByKey reduceBykey sortBy sortByKey join[cogroup left inner right] shuffle的 mapValues keys values flatMapValues 普通算子&#xff0c;管道形式的算子 shuffle的过程是因为数据产生了打乱重分&#xff0c;分组、排序、join等…

计算机网络基础全攻略:探秘网络构建块(1/10)

一、计算机网络基础概念 计算机网络是指将地理位置不同的具有独立功能的多台计算机及其外部设备&#xff0c;通过通信线路和通信设备连接起来&#xff0c;在网络操作系统&#xff0c;网络管理软件及网络通信协议的管理和协调下&#xff0c;实现资源共享和信息传递的计算机系统…

游戏陪玩系统开发功能需求分析

电竞游戏陪玩系统是一种专门为游戏玩家提供陪伴、指导和互动服务的平台。这类系统通常通过专业的陪玩师&#xff08;也称为陪练师&#xff09;为玩家提供一对一或多对一的游戏陪伴服务&#xff0c;帮助玩家提升游戏技能、享受游戏乐趣&#xff0c;甚至解决游戏中的各种问题。电…

关于SpringBoot集成Kafka

关于Kafka Apache Kafka 是一个分布式流处理平台&#xff0c;广泛用于构建实时数据管道和流应用。它能够处理大量的数据流&#xff0c;具有高吞吐量、可持久化存储、容错性和扩展性等特性。 Kafka一般用作实时数据流处理、消息队列、事件架构驱动等 Kafka的整体架构 ZooKeeper:…

Linux 下的IO模型

一&#xff1a;四种IO模 1.1&#xff1a;阻塞式IO&#xff08;最简单&#xff0c;最常用&#xff0c;效率最低&#xff09; 阻塞I/O 模式是最普遍使用的I/O 模式&#xff0c;大部分程序使用的都是阻塞模式的I/O 。 缺省情况下&#xff08;及系统默认状态&#xff09;&#xf…

vue3项目部署在阿里云轻量应用服务器上

文章目录 概要整体部署流程技术细节小结 概要 vue3前端项目部署在阿里云轻量服务器 整体部署流程 首先有一个Vue3前端项目和阿里云应用服务器 确保环境准备 如果是新的服务器&#xff0c;在服务器内运行以下命令更新软件包 sudo apt update && sudo apt upgrade -y …

tcpdump交叉编译

TCPDUMP在Libpcap上开发。 首先需要编译libcap。 网上那么多教程&#xff0c;下载地址都只给了一个英文的官网首页&#xff0c; 你尽可以试试&#xff0c;从里面找到下载地址都要费半天时间。 \color{red}网上那么多教程&#xff0c;下载地址都只给了一个英文的官网首页&#…

KubeSphere 最佳实战:K8s 构建高可用、高性能 Redis 集群实战指南

首发&#xff1a;运维有术。 本指南将逐步引导您完成以下关键任务&#xff1a; 安装 Redis&#xff1a;使用 StatefulSet 部署 Redis。自动或手动配置 Redis 集群&#xff1a;使用命令行工具初始化 Redis 集群。Redis 性能测试&#xff1a;使用 Redis 自带的 Benchmark 工具进…

02 python基础 python解释器安装

首先在网站&#xff1a;Welcome to Python.org进行下载安装python 最新的解释器不一定是最好的&#xff0c;最稳定的才一定是最好的&#xff1b;要关注解释器最后维护 的时间。 一、python的安装 python安装的时候一定要在下载勾选好添加path环境 安装的时候尽量选择好自己的安…

java编程开发基础,正则表达式的使用案例Demo

java编程开发基础,正则表达式的使用案例Demo!实际开发中&#xff0c;经常遇到一些字符串&#xff0c;信息的裁剪和提取操作&#xff0c;正则表达式是经常使用的&#xff0c;下面的案例&#xff0c;可以帮助大家快速的了解和熟悉&#xff0c;正则表达式的使用技巧。 package com…

Windows Pycharm 远程 Spark 开发 PySpark

一、环境版本 环境版本PyCharm2024.1.2 (Professional Edition)Ubuntu Kylin16.04Hadoop3.3.5Hive3.1.3Spark2.4.0 二、Pycharm远程开发 文件-远程-开发 选择 SSH连接&#xff0c;连接虚拟机&#xff0c;选择项目目录即可远程开发

WebGL进阶(十一)层次模型

理论基础&#xff1a; 效果&#xff1a; 源码&#xff1a; <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><meta http-equiv"X-UA-Compatible" content"IEedge"><meta name"vie…

【H2O2|全栈】JS进阶知识(九)ES6(5)

目录 前言 开篇语 准备工作 class类 概念 形式 直接继承 概念 优点 案例 重写 概念 案例 关于重载 结束语 前言 开篇语 本系列博客主要分享JavaScript的进阶语法知识&#xff0c;本期为第九期&#xff0c;依然围绕ES6的语法进行展开。 本期内容为&#xff1a…

Prompting LLMs to Solve Complex Tasks: A Review

文章目录 题目简介任务分解未来方向结论 题目 促使 LLM 解决复杂任务&#xff1a; 综述 论文地址&#xff1a;https://www.intjit.org/cms/journal/volume/29/1/291_3.pdf 简介 大型语言模型 (LLM) 的最新趋势显而易见&#xff0c;这体现在大型科技公司的投资以及媒体和在线社…

学会Lambda,让程序Pythonic一点

Lambda是Python里的高阶用法&#xff0c;要把代码写得Pythonic&#xff0c;就需要了解这些高阶用法&#xff0c;想说自己是一名真正的Python程序员&#xff0c;先要把代码写得Pythonic。 今天聊下Lambda的用法&#xff0c;写篇简短的用法说明。 Lambda是匿名函数的意思&#…