当前位置: 首页 > news >正文

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

reserve(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.mrgr.cn/news/78624.html

相关文章:

  • [蜀道山2024] PWN
  • 数据结构---链表
  • 力扣162:寻找峰值
  • GitLab 使用过程中常见问题及解决方案
  • 计算机的错误计算(一百六十八)
  • ctfshow -web -118-124
  • HCIA笔记6--路由基础
  • 【真正离线安装】Adobe Flash Player 32.0.0.156 插件离线安装包下载(无需联网安装)
  • 透视投影(Perspective projection)与等距圆柱投影(Equirectangular projection)
  • GateWay使用手册
  • gcc编译
  • 如何在Spark中使用gbdt模型分布式预测
  • HTML飞舞的爱心(完整代码)
  • HarmonyOS Next 模拟器安装与探索
  • 十四(AJAX)、AJAX、axios、常用请求方法(GET POST...)、HTTP协议、接口文档、form-serialize
  • 基于vite创建一个脚手架(快速入门)
  • 【Gitlab】CICD使用minio作为分布式缓存
  • 【OJ】前K个高频单词和单词识别和两个数组的交集
  • PyG教程:MessagePassing基类
  • Java ConcurrentHashMap
  • HTTP 1
  • Java Collection
  • uniapp连接mqtt频繁断开原因和解决方法
  • 【组成原理】计算机硬件设计——ALU
  • Maven 配置
  • yolov8的深度学习环境安装(cuda12.4、ubuntu22.04)