18. C++STL 4(vector的使用, 空间增长, 迭代器失效详解)

⭐本篇重点:vector容器的使用详解

⭐本篇代码:c++学习/08.vector_test · 橘子真甜/c++-learning-of-yzc - 码云 - 开源中国 (gitee.com)

目录

一. vector的介绍

二. vector的使用 

2.1 vector的定义 *

2.2 vector的迭代器和遍历

a operator[]访问

b vector迭代器的使用访问 *

c C++11 for循环

2.3  vector的容量操作和空间成长问题

a 容量操作:

b 空间增长问题  ***

2.4 vector的增删查改

 三. 使用vector出现的迭代器失效问题 *

3.1 增容导致迭代器失效

 3.2 erase导致迭代器失效


一. vector的介绍

vector的文档如下:vector - C++ Reference (cplusplus.com)

        1 vector就是一个可以动态增长的数组。和数组一样,可以使用[下标]来访问vector中存储的数据。

        2 vector动态分配数组来存储他的元素。每当插入新元素的时候,空间足够就会在数组尾部插入这个数据。如果空间不够,vector会重新分配一个容量更大的数组,然后将全部元素转移到这个新的数组中

        3 vector为了方便管理空间,每一次分配空间的时候都比实际要存储的空间要大一些。不同的系统和编译器增长的倍数有区别,一般都在1.5倍到2倍数

        4 相对其他序列容器(list, deque, forward_list),vector访问数据的效率更高,在末尾插入删除数据效率高。在头部和中部插入删除数据由于要大量挪动数据,效率较低

二. vector的使用 

2.1 vector的定义 *

vector构造函数的列表如下: 标*表示重要

构造函数的声明说明
vector(); *一个简单的无参构造函数
vector(const vector& x); *拷贝构造函数
vector(size_type n, const value_type& val = value_type())构造并初始化为n个val
vector (InputIterator first, InputIterator last);使用迭代器进行构造

赋值运算符

vector& operator=(const vector& x)使用x这个容器去赋值

2.2 vector的迭代器和遍历

        vector有三种遍历方式

a operator[]访问

#define _CRT_SECURE_NO_WARNINGS 1
#include <iostream>
#include <vector>	//使用vector需要的头文件
using namespace std;int main()
{//{}内部的数据可以看成为一个vectorvector<int> arr1 = { 1,5,9,7,5,3,4,6,8,2 };vector<int> arr2(arr1);	//使用拷贝构造函数构造vector//1. []访问遍历,可以读写数据		arr1.size()可以获取arr1中元素的大小for (int i = 0; i < arr1.size(); i++){cout << arr1[i] << " ";}cout << endl;//访问arr2for (int i = 0; i < arr1.size(); i++){cout << arr1[i] << " ";}cout << endl;return 0;
}

运行结果如下 

b vector迭代器的使用访问 *

可以通过begin/end获取vector的迭代器

iterator解释
begin()获取第一个数据位置 iterator/ const_iterator
end()获取最后一个数据位置 iterator/ const_iterator
rbegin()获取最后一个数据位置 reverse_iterator
rend()获取第一个数据位置的 reverse_iterator

 迭代遍历如下

#define _CRT_SECURE_NO_WARNINGS 1
#include <iostream>
#include <vector>	//使用vector需要的头文件
using namespace std;int main()
{//{}内部的数据可以看成为一个vectorvector<int> arr = { 1,5,9,7,5,3,4,6,8,2 };//2.迭代器正向访问vector<int>::iterator it = arr.begin();while (it != arr.end()){cout << *it << " ";it++; //获取下一个位置的迭代器}cout << endl;//迭代器逆向访问vector<int>::reverse_iterator rit = arr.rbegin();while (rit != arr.rend()){cout << *rit << " ";rit++;}return 0;
}

运行结果: 

当然:我们还能通过迭代器修改数据

c C++11 for循环

#define _CRT_SECURE_NO_WARNINGS 1
#include <iostream>
#include <vector>	//使用vector需要的头文件
using namespace std;int main()
{//{}内部的数据可以看成为一个vectorvector<int> arr = { 1,5,9,7,5,3,4,6,8,2 };//C++ 11 for循环for (const auto& e : arr)cout << e << " ";cout << endl;return 0;
}

运行结果 

这种for循环的本质也是通过迭代器实现的!

2.3  vector的容量操作和空间成长问题

a 容量操作:

操作说明
size()获取这个vector的元素大小
capacity()获取这个vectro的最大容量
empty()判断这个vector是否为空

resize(size_type n)

resize(size_type n, value_type& val)

将vector的size更改为 n 

将vector的size更改为 n, 值为 val

reverse(size_type n)将的vector的capacity更改为 n

b 空间增长问题  ***

        vector为了方便管理空间,每次开辟的空间都比实际的容量要多 并且不同的编译器和系统下,容量增长的倍数是不一样的. 一般是1.5倍 到 2倍数

        经过测试,我发现在VS2022中. 容量增长的倍数是1.5倍

        在Linux (cent os) 中容量增长是2倍

测试代码和结果如下:

vector容量增长倍数经过测试在Windows下(VS2022)中是1.5倍

#include <iostream>
#include <vector>
using namespace std;int main()
{int capacity = 0;vector<int> arr;//不断插入数据并计算其容量,观察容量变化for (int i = 0; i < 100; i++){if (capacity != arr.capacity()){capacity = arr.capacity();cout << capacity << " ";}arr.push_back(i);}return 0;
}

运行结果如下:

可以看到容量的增长倍数是1.5倍

 在VS2022查找vector的push_back源码, 可以看到下面这段代码

在Linux测试的结果如下: 

        vector每一次插入数据的时候,如果size < capacity 就会直接在后面插入数据.

        如果size > capacity 那么vector就会重新开辟一份更大的空间,将原来是数据全部拷贝到新的空间中, 再去插入数据 .

 测试代码:

#include <iostream>
#include <vector>
using namespace std;int main()
{vector<int> arr;arr.push_back(1);printf("%p\n", &arr[0]); //1个数据打印arr[0]的地址arr.push_back(1);arr.push_back(1);arr.push_back(1);arr.push_back(1);printf("%p\n", &arr[0]); //5个数据打印arr[0]的地址return 0;
}

 运行结果如下:可以看到其地址发生了改变

        这说明, vector如果频繁的去push_back就会不断拷贝复制,导致效率降低

        为了解决这种问题:我们使用vector的时候最好提前预估需要使用的容量, 并且使用resize或者reverse去设置好容量

2.4 vector的增删查改

        学习STL容器基本都要掌握容器的增删查改和底层设计

vector常用增删插改如下:

容量操作说明
push_back()在vector尾部插入一个数据
pop_back()在vector尾部删除一个数据
find() 

在vector中查找一个数据,返回迭代器

这个是STL算法提供的, vector内部没有

insert()在输入的pos位置前面插入一个数据
erase()在输入的pos位置删除一个数据
swap()交换两个元素
operator[]像数组一样可以访问和修改指定下标的元素

测试代码:

#define _CRT_SECURE_NO_WARNINGS 1
#include <iostream>
#include <vector>	//使用vector需要的头文件
using namespace std;void print(const vector<int>& arr)
{for (const auto& e : arr)cout << e << " ";cout << endl;
}int main()
{//增删查改vector<int> arr;arr.push_back(1); //1arr.push_back(2); //1 2 arr.pop_back();	  //1arr.push_back(3); //1 3arr.push_back(4); //1 3 4print(arr);		//结果应该为: 1 3 4auto pos = find(arr.begin(), arr.end(), 3);arr.insert(pos, 5);	//在3的前面插入5  1 5 3 4pos = find(arr.begin(), arr.end(), 4);arr.erase(pos);	//删除pos位置的4print(arr);	//结果应该为: 1 5 3return 0;
}

测试结果如下:

 三. 使用vector出现的迭代器失效问题 *

3.1 增容导致迭代器失效

 先看一份代码:

#define _CRT_SECURE_NO_WARNINGS 1
#include <iostream>
#include <vector>	//使用vector需要的头文件
using namespace std;int main()
{vector<int> arr = { 1,2,3,4,5 };auto it = arr.begin();arr.push_back(6);arr.push_back(7);while (it != arr.end()){cout << *it << " ";it++;}return 0;
}

这份代码看着好像没啥问题,但是在运行之后去报错了!

 

经过调试之后,发现是迭代器非法访问

调试过程如下:

 

        经过调试知道和vector增容的机制可以知道:

设置好一个迭代器后,vector如果发生了增容. 就会销毁旧空间,开辟新空间

而it仍然指向旧的空间, 这个一来*it就会非法访问.

所以我们使用push_back,insert,reverse,resize这些接口的时候.

要注意调用这些接口之前的迭代器不可使用.

调用接口之后要重新定义迭代器进行使用

 3.2 erase导致迭代器失效

同样先看一份代码:

#define _CRT_SECURE_NO_WARNINGS 1
#include <iostream>
#include <vector>	//使用vector需要的头文件
using namespace std;int main()
{//我们想要删除arr中的偶数vector<int> arr = { 1,2,3,4,5,6 };auto it = arr.begin();while (it != arr.end()){if ((*it) % 2 == 0){arr.erase(it);}it++;}for (const auto& e : arr)cout << e << " ";cout << endl;return 0;
}

我们想要删除偶数,运行的时候却崩溃了

        经过简单分析:在我们删除数据之后,it就失效了

因为删除it之后,it指向的位置就不对了

 

 

可以看到报错

一般来说:我们解决迭代器失效的方法是 对迭代器重新赋值

#define _CRT_SECURE_NO_WARNINGS 1
#include <iostream>
#include <vector>	//使用vector需要的头文件
using namespace std;int main()
{//我们想要删除arr中的偶数vector<int> arr = { 1,2,3,4,5,6 };auto it = arr.begin();while (it != arr.end()){if ((*it) % 2 == 0){//对迭代器重新赋值,erase删除数据后会返回删除数据后面一个数据的迭代器。//重新赋值之后就不会出错了!it = arr.erase(it);}else{it++; //为奇数,直接it增加}}for (const auto& e : arr)cout << e << " ";cout << endl;return 0;
}

 

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

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

相关文章

深入探索机器学习性能优化的关键路径——《特征工程训练营》

通过“特征工程”技术&#xff0c;可优化训练数据&#xff0c;提升机器学习流程的输出效果&#xff01;“特征工程”基于现有数据设计相关的输入变量&#xff0c;由此简化训练过程&#xff0c;增强模型性能。调整超参数或模型的效果都不如特征工程&#xff1b;特征工程通过改变…

吉他初学者学习网站搭建系列(8)——如何练习音阶

文章目录 背景实现吉他面板音阶位置音阶识别 结语 背景 大家好&#xff0c;我是一个爱好音乐的非典型程序员&#xff01;我最近又往自己的网站中集成了一个模块——音阶。下面介绍一下背景。 很多吉他初学者在掌握了一些音阶知识后&#xff0c;可能不知道怎么训练自己的对音阶…

15.三数之和 python

三数之和 题目题目描述示例 1&#xff1a;示例 2&#xff1a;示例 3&#xff1a;题目链接 题解Python 实现解释提交结果 题目 题目描述 给你一个整数数组 nums &#xff0c;判断是否存在三元组 [nums[i], nums[j], nums[k]] 满足 i ! j、i ! k 且 j ! k &#xff0c;同时还满…

tauri使用github action打包编译多个平台arm架构和inter架构包踩坑记录

这些error的坑&#xff0c;肯定是很多人不想看到的&#xff0c;我的开源软件PakePlus是使用tauri开发的&#xff0c;PakePlus是一个界面化将任何网站打包为轻量级跨平台软件的程序&#xff0c;利用Tauri轻松构建轻量级多端桌面应用和多端手机应用&#xff0c;为了实现发布的时候…

Android 12.0 DocumentsUI文件管理器首次进入默认显示内部存储文件功能实现

1.前言 在12.0的系统rom定制化开发中,在关于文件管理器的某些功能中,在首次进入文件管理器的时候默认进入下载 文件夹,点击菜单选择内部存储的时候,会显示内部存储的内容,客户开发需要要求默认显示内部存储的文件 接下来分析下功能的实现 如图: 2.DocumentsUI文件管理器首…

抓包之wireshark基础用法介绍

写在前面 wireshark作为最优秀的抓包工具&#xff0c;有必要详细的看下其基本用法&#xff0c;所以本文就一起来做这件事吧&#xff01; 1&#xff1a;初步介绍 打开wireshark首先会进入如下的界面&#xff1a; 想要开始抓包&#xff0c;需要进行如下操作&#xff1a; 接着…

【Java基础入门篇】二、控制语句和递归算法

Java基础入门篇 二、控制语句和递归算法 2.1 switch-case多分支选择语句 switch执行case语句块时&#xff0c;若没有遇到break&#xff0c;则运行下一个case直到遇到break&#xff0c;最后的default表示当没有case与之匹配时&#xff0c;默认执行的内容&#xff0c;代码示例如…

【人工智能学习之STGCN训练自己的数据集】

STGCN训练自己的数据集 准备事项数据集制作视频转jsonjsons转jsonjson转npy&pkl 训练STGCN添加图结构修改训练参数开始训练测试 准备事项 st-gcn代码下载与环境配置 git clone https://github.com/yysijie/st-gcn.git cd st-gcn pip install -r requirements.txt cd torc…

Dify+Docker

1. 获取代码 直接下载 &#xff08;1&#xff09;访问 langgenius/dify: Dify is an open-source LLM app development platform. Difys intuitive interface combines AI workflow, RAG pipeline, agent capabilities, model management, observability features and more, …

Android so库的编译

在没弄明白so库编译的关系前,直接看网上博主的博文,常常会觉得云里雾里的,为什么一会儿通过Android工程cmake编译,一会儿又通过NDK命令去编译。两者编译的so库有什么区别? android版第三方库编译总体思路: 对于新手小白来说搞明白上面的总体思路图很有必…

Java函数式编程+Lambda表达式

文章目录 函数式编程介绍纯函数Lambda表达式基础Lambda的引入传统方法1. 顶层类2. 内部类3. 匿名类 Lambda 函数式接口&#xff08;Functional Interface&#xff09;1. **函数式接口的定义**示例&#xff1a; 2. **函数式接口与Lambda表达式的关系**关联逻辑&#xff1a;示例&…

Linux操作系统2-进程控制3(进程替换,exec相关函数和系统调用)

上篇文章&#xff1a;Linux操作系统2-进程控制2(进程等待&#xff0c;waitpid系统调用&#xff0c;阻塞与非阻塞等待)-CSDN博客 本篇代码Gitee仓库&#xff1a;Linux操作系统-进程的程序替换学习 d0f7bb4 橘子真甜/linux学习 - Gitee.com 本篇重点&#xff1a;进程替换 目录 …

文件上传漏洞:你的网站安全吗?

文章目录 文件上传漏洞攻击方式&#xff1a;0x01绕过前端限制0x02黑名单绕过1.特殊解析后缀绕过2..htaccess解析绕过3.大小写绕过4.点绕过5.空格绕过6.::$DATA绕过7.配合中间件解析漏洞8.双后缀名绕过9.短标签绕过 0x03白名单绕过1.MIME绕过(Content-Type绕过)2.%00截断3.0x00截…

设计模式-适配器模式-注册器模式

设计模式-适配器模式-注册器模式 适配器模式 如果开发一个搜索中台&#xff0c;需要适配或接入不同的数据源&#xff0c;可能提供的方法参数和平台调用的方法参数不一致&#xff0c;可以使用适配器模式 适配器模式通过封装对象将复杂的转换过程隐藏于幕后。 被封装的对象甚至…

springboot341+vue校园求职招聘系统设计和实现pf(论文+源码)_kaic

毕 业 设 计&#xff08;论 文&#xff09; 校园求职招聘系统设计与实现 摘 要 传统办法管理信息首先需要花费的时间比较多&#xff0c;其次数据出错率比较高&#xff0c;而且对错误的数据进行更改也比较困难&#xff0c;最后&#xff0c;检索数据费事费力。因此&#xff0c;…

基于java web的网上书店系统设计

摘 要 随着互联网的越发普及&#xff0c;网上购物成为了当下流行的热门行为。网络上开店创业有许多的优势&#xff1a;投入少&#xff0c;启动 资金低&#xff0c;交易便捷。网上书店与传统的线下书店比起来优势巨大&#xff0c;网上书店的经营方式和销售渠道是不同与线下书 店…

Java设计模式——职责链模式:解锁高效灵活的请求处理之道

嘿&#xff0c;各位 Java 编程大神和爱好者们&#xff01;今天咱们要一同深入探索一种超厉害的设计模式——职责链模式。它就像一条神奇的“处理链”&#xff0c;能让请求在多个对象之间有条不紊地传递&#xff0c;直到找到最合适的“处理者”。准备好跟我一起揭开它神秘的面纱…

Android 设备使用 Wireshark 工具进行网络抓包

背景 电脑和手机连接同一网络&#xff0c;想使用wireshark抓包工具抓取Android手机网络日志&#xff0c;有以下两种连接方法&#xff1a; Wi-Fi 网络抓包。USB 网络共享抓包。需要USB 数据线将手机连接到电脑&#xff0c;并在开发者模式中启用 USB 网络共享。 查看设备连接信…

redis大key和热key

redis中大key、热key 什么是大key大key可能产生的原因大key可能会造成什么影响如何检测大key如何优化删除大key时可能的问题删除大key的策略 热key热key可能导致的问题解决热key的方法 什么是大key 大key通常是指占用内存空间过大或包含大量元素的键值对。 数据量大&#xff…

SpringBoot源码-spring boot启动入口ruan方法主线分析(二)

12.刷新前操作 // 刷新前操作prepareContext(context, environment, listeners, applicationArguments, printedBanner);进入prepareContext private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment,SpringApplicationRun…