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.rhkb.cn/news/477101.html

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

相关文章

libphone desktop编译

linphone-desktop 在ubuntu20.04 下编译 linphone 介绍 Linphone是一款遵循GPL的开源网络视频电话系统&#xff0c;支持多种平台如Windows、Linux、Android等。它基于SIP协议&#xff0c;提供语音、视频通话及即时文本消息功能。核心功能包括SIP用户代理、音频视频Codec支持、…

根据已知站点寻找路网的最短路径

背景 接上期&#xff0c;基于MATSim的交通仿真&#xff0c;其中有一块非常重要的就是公交的仿真&#xff0c;这也是当初选择MATSim技术路线的一个重要原因&#xff0c;现在业务给出的场景是上传一些有序站点及其经纬度&#xff0c;需要通过算法来适配对应的路网&#xff0c;由…

Jenkins + gitee 自动触发项目拉取部署(Webhook配置)

目录 前言 Generic Webhook Trigger 插件 下载插件 ​编辑 配置WebHook 生成tocken 总结 前言 前文简单介绍了Jenkins环境搭建&#xff0c;本文主要来介绍一下如何使用 WebHook 触发自动拉取构建项目&#xff1b; Generic Webhook Trigger 插件 实现代码推送后&#xff0c;触…

leetcode 919.完全二叉树插入器

1.题目要求: 完全二叉树 是每一层&#xff08;除最后一层外&#xff09;都是完全填充&#xff08;即&#xff0c;节点数达到最大&#xff09;的&#xff0c;并且所有的节点都尽可能地集中在左侧。设计一种算法&#xff0c;将一个新节点插入到一棵完全二叉树中&#xff0c;并在…

SSL协议

文章目录 1. 前言2. 基础概念3. SSL协议结构3.1 概述3.2 SSL握手协议3.3 修改密码说明协议3.4 报警协议3.5 SSL记录协议 4. SSL安全性4.1 安全机制分析4.2 脆弱性分析 5. SSL证书 1. 前言 参考《应用系统安全基础》 2. 基础概念 安全套接字层协议&#xff08;Security Socke…

Flink-Source的使用

Data Sources 是什么呢&#xff1f;就字面意思其实就可以知道&#xff1a;数据来源。 Flink 做为一款流式计算框架&#xff0c;它可用来做批处理&#xff0c;也可以用来做流处理&#xff0c;这个 Data Sources 就是数据的来源地。 flink在批/流处理中常见的source主要有两大类…

Linux线程_线程控制_线程库

一.线程控制 在Linux操作系统的视角&#xff0c;Linux下没有真正意义上的线程&#xff0c;而是用进程模拟的线程&#xff08;LWP&#xff09;。所以&#xff0c;Linux不会提供直接创建线程的系统调用&#xff0c;而是提供创建轻量级进程的接口。但是由于用户只认线程&#xff0…

计算机网络:运输层 —— TCP 的超时重传机制

文章目录 TCP 的超时重传超时重传时间的选择重传策略与拥塞控制的关联 TCP 的超时重传 TCP 的超时重传是保证数据可靠传输的重要机制之一 保证数据可靠性&#xff1a;通过超时重传机制&#xff0c;即使在网络状况不佳&#xff0c;出现数据包丢失等情况时&#xff0c;也能够确保…

C嘎嘎探索篇:和stack,queue的相遇

C嘎嘎探索篇&#xff1a;和stack&#xff0c;queue的再次相遇 前言&#xff1a; 小编在前几日刚完成了关于list容器的介绍&#xff0c;中间由于我牙齿出现了问题所以断更了不少天&#xff0c;如今我牙齿已经恢复&#xff0c;我也要开始继续新内容的讲解了&#xff0c;各位读者…

GPTZero:高效识别AI生成文本,保障学术诚信与内容原创性

产品描述 GPTZero 是一款先进的AI文本检测工具&#xff0c;专为识别由大型语言模型&#xff08;如ChatGPT、GPT-4、Bard等&#xff09;生成的文本而设计。它通过分析文本的复杂性和一致性&#xff0c;判断文本是否可能由人类编写。GPTZero 已经得到了超过100家媒体机构的报道&…

MyBatis Plus 项目的创建和使用

1. 快速上手 1.1. 项目的创建和配置 首先&#xff0c;创建一个 Spring Boot 工程&#xff0c;添加 MyBatis Plus 和 MySQL 对应的依赖&#xff0c;然后&#xff0c;和 MyBatis 一样&#xff0c;需要在 yml 文件中配置数据库连接信息 <dependency><groupId>com.b…

IDEA 2024.3 版本更新主要功能介绍

IDEA 2024.3 版本提供的新特性 IntelliJ IDEA 2024.3 的主要新特性&#xff1a; AI Assistant 增强 改进的代码补全和建议更智能的代码分析和重构建议Java 支持改进 支持 Java 21 的所有新特性改进的模式匹配和记录模式支持更好的虚拟线程调试体验开发工具改进 更新的 UI/UX 设…

Java编程,配置mongoUri连接mongodb时,需对特殊字符进行转义

一、背景 java程序连接mongo有两种方式&#xff1a; 用户名和密码方式uri方式 1、用户名和密码 以用户数据库为例&#xff0c;注意看它的密码 spring:data:mongodb:host: 192.168.10.17database: db_user_serviceport: 3717username: user_servicepassword: user_service3…

学习笔记|MaxKB对接本地大模型时,选择Ollma还是vLLM?

在使用MaxKB开源知识库问答系统的过程中&#xff0c;除了对接在线大模型&#xff0c;一些用户出于资源配置、长期使用成本、安全性等多方面考虑&#xff0c;还在积极尝试通过Ollama、vLLM等模型推理框架对接本地离线大模型。而在用户实践的过程中&#xff0c;经常会对候选的模型…

Python 快速入门(上篇)❖ Python基础知识

Python 基础知识 Python安装**运行第一个程序:基本数据类型算术运算符变量赋值操作符转义符获取用户输入综合案例:简单计算器实现Python安装** Linux安装: yum install python36 -y或者编译安装指定版本:https://www.python.org/downloads/source/ wget https://www.pyt…

【1.2 Getting Started--->Installation Guide】

NVIDIA TensorRT DOCS 此 NVIDIA TensorRT 10.6.0 安装指南提供安装要求、TensorRT 包中包含的内容列表以及安装 TensorRT 的分步说明。 安装指南 摘要&#xff1a; 本 NVIDIA TensorRT 10.3.0 安装指南提供了安装要求、TensorRT 软件包中包含的内容列表以及安装 TensorRT 的…

RT_Thread内核源码分析(三)——线程

目录 1. 线程结构 2. 线程创建 2.1 静态线程创建 2.2 动态线程创建 2.3 源码分析 2.4 线程内存结构 3. 线程状态 3.1 线程状态分类 3.2 就绪状态和运行态 3.3 阻塞/挂起状态 3.3.1 阻塞工况 3.4 关闭状态 3.4.1 线程关闭接口 3.4.2 静态线程关闭 3.4.3 动态线程关…

Unity图形学之CubeMap立方体贴图

1.CubeMap&#xff1a;有六个面的贴图组成 2. 假反射&#xff1a;反射天空盒子 &#xff08;1&#xff09;正常UV采样&#xff1a; &#xff08;2&#xff09;Cube的采样&#xff1a;利用反射角采样&#xff0c;反射角X和Cube的交点采样 Shader "Custom/TestReflect"…

C语言基础学习:抽象数据类型(ADT)

基础概念 抽象数据类型&#xff08;ADT&#xff09;是一种数据类型&#xff0c;它定义了一组数据以及可以在这组数据上执行的操作&#xff0c;但隐藏了数据的具体存储方式和实现细节。在C语言中&#xff0c;抽象数据类型&#xff08;ADT&#xff09;是一种非常重要的概念&…

Qt-多元素控件

Qt中的多元素控件 Qt提供的多元素控件有&#xff1a; 这里的多元素控件都是两两一对的。 xxWidget和xxView的一个比较简单的理解就是&#xff1a; xxView是更底层的实现&#xff0c; xxWidget是基于xxView封装来的。 可以说&#xff0c;xxView使用起来比较麻烦&#xff0c;但…