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

STL之vecor的使用(超详解)

 

目录

 1. C/C++中的数组

1.1. C语言中的数组

1.2. C++中的数组 

 2. vector的接口

2.1. vector的迭代器 

2.2. vector的初始化与销毁 

2.3. vector的容量操作 

2.4. vector的访问操作 

2.5. vector的修改操作 


💓 博客主页:C-SDN花园GGbond

⏩ 文章专栏:玩转c++

 1. C/C++中的数组

1.1. C语言中的数组

在 C 语言中,数组是一组相同类型元素的有序集合。与字符串类似,它的大小在编译时就已经确定,不可更改。 

//大小为5的整型数组
int arr1[5] = { 1,2,3,4,5 };
//大小为5的浮点型数组
double arr2[5] = {0.0};

1.2. C++中的数组 

同样与string类似,C++为了更加方便就引入了一个支持可动态大小数组的序列容器vecotr。其特点如下:

 

  1. vector是可变大小的序列容器,采用连续存储空间存储元素,可通过下标高效访问。
  2. 与数组不同,vector大小可动态改变,由容器自动处理。
  3. vector本质上用动态分配数组存储元素,插入新元素时可能重新分配空间,即分配新数组并移动全部元素,此操作时间代价高,但不是每次插入都重新分配。
  4. vector会分配额外空间适应增长,不同库策略不同,但重新分配通常是对数增长间隔,使末尾插入元素能在常数时间完成。
  5. dequelistforward_list相比,vector访问元素及末尾添加和删除元素更高效,非末尾的删除和插入操作效率低,且统一的迭代器和引用更好。
//整型数组
vector<int> v1;
//浮点型数组
vector<double> v2;

使用vector都需要包含头文件#include<vector>。并且vector是一个模版类,所以在使用时需要显示实例化。 

 

 2. vector的接口

介绍一些vector的常见接口,因为很多接口的作用都与string的接口非常类似,所以很多就不在详细说明,具体也可以参考vector文档。

4cf179dc487747cbb791b073911b00f3.png

  下面我们开始研究他的使用,为了能够更好的测试,我们先实现一个打印容器元素的函数,vector底层是数组,所以有三种访问方式:下标访问、迭代器访问、范围for(本质也是迭代器)

void Printf(vector<int>& v)
{//下表遍历cout << "下表遍历:";for (size_t i = 0; i < v.size(); i++){cout << v[i];}cout << endl;//迭代器遍历cout << "迭代器遍历:";vector<int>::iterator it = v.begin();while (it != v.end()){cout << *it;it++;}cout << endl;cout << "范围for遍历:";for (auto e : v){cout << e;}cout << endl;
}
int main()
{vector<int> v = { 1,2,3,4,5,6,7,8,9 };Printf(v);//Test01();return 0;
}

 57eac87fa68d443db7ed5129175398f1.png

 ​​​​​​

2.1. vector的迭代器 

同样的vector中也存在迭代器iterator,因为定义在vector类中,所以其需要通过域作用限定符访问——vector<类型>::iterator

下面将介绍的begin(),end(),rbeign(),rend()的使用访问方法与string中的几乎一摸一样,我们直接上实例演示:

#include<iostream>
#include<vector>using namespace std;
void Test01()
{vector<int> v = { 1,2,3,4,5,6,7,8,9 };cout << "顺序遍历:";vector<int>::iterator it = v.begin();while (it != v.end()){cout << *it;it++;}cout << endl;cout << "逆序遍历:";vector<int>::reverse_iterator rit = v.rbegin();while (rit != v.rend()){cout << *rit;rit++;}cout << endl;}
int main()
{Test01();return 0;
}

093772b6c25f4fd2a8634c094427e8ec.png

 

当然vector也支持const_iterator,用法也类似,这里就不在赘述。 

2.2. vector的初始化与销毁 

60befd5fa70d4c28b67fc30196b9e7c5.png

(1) 空容器构造函数(默认构造函数)
构造一个没有元素的空容器。
(2) 填充构造函数
构造一个包含n个元素的容器。每个元素都是val的副本。
(3) 范围构造器
构造一个包含与范围[first,last)一样多的元素的容器,每个元素都按照相同的顺序从该范围内的相应元素构造而成。
(4) 复制构造函数
构造一个容器,其中包含x中每个元素的副本,顺序相同。
 

c7001ccbe26d422da35a785a836479e1.png

同样的vector也支持多种构造函数,拷贝构造以及赋值运算符重载。 

void Test02()
{//无参构造vector<int> v1;Print(v1);//有参构造,n个位置初始化vector<int> v2(5, 2);Print(v2);//有参构造,n个位置调用T类型的默认构造vector<int> v3(5);Print(v3);//拷贝构造vector<int> v4(v3);Print(v4);//迭代器区间构造(传string的迭代器区间)string s("hello world");vector<int> v5(s.begin(), s.end());Print(v5);//迭代器区间构造(传vctor的迭代器区间)vector<int> v6(v5.begin(), v5.end());Print(v6);//赋值重载v1 = v6;cout << &v1 << "   " << &v6 << endl;//深拷贝Print(v1);//特殊的赋值方式vector<int> v7{ 1,2,3,4,5,6,7,8 };Print(v7);
}

98d091b952164f39b9eb1bc71e6ac6b2.png

2.3. vector的容量操作 

0f433b82d99d4bb9b03c7e754cc93593.png

d4c5b5393fea4b67a25b66ef3754eb11.png

vector类中,同样可以通过size()容器的有效长度;capacity()返回容器的容量大小。

void Test03()
{vector<int> v1;cout << v1.size() << endl;cout << v1.capacity() << endl;vector<int> v2 = { 0,1,2,3,4,5 };cout << v2.size() << endl;cout << v2.size() << endl;
}

12b8ab8d05524637b37183d3a5064fa4.png

在初始化时,vecotr中的sizecapacity一般相同。这时我们也可以通过以下程序探究一下其扩容机制: 

void Test04()
{size_t sz;vector<int> v;sz = v.capacity();cout << "making v grow" << endl;for (int i = 0; i < 100; i++){v.push_back(i);if (sz != v.capacity()){sz = v.capacity();cout << "capacity changed  " << v.capacity() << endl;}}
}
int main()
{Test04();return 0;
}

c34dee28ec704cb29486fb593477407d.png

在VS环境下,vector一般是以1.5倍扩容。但是在Linux环境下一般就以2倍扩容。 

58c314016a714161b67b9c59b0c61a4a.png

a2458ec7c562403b807c6a0ea95b76ba.png

要求容器减少容量以适应其大小。

该请求是非绑定的,容器实现可以自由地进行优化,使向量的容量大于其大小。

这可能会导致重新分配,但对向量大小没有影响,也不能改变其元素。

 

ae966670b2264115809eafc566891532.png

 

有效长度与容量操作 

 vector中的resize()reserve()。其实他们的用法与特点也是与string类中的相同

当n<sz时,reserve并不会发生任何改变,resize会删除有效字符到指定大小。
当sz<n<capcity时,reserve并不会发生任何改变,resize会补充有效字符(默认为0)到指定大小。
当n>capacity时,reserve会发生扩容,resize会补充有效字符(默认为0)到指定大小。

void Test06()
{vector<int> v1 = { 1,2,3,4,5 };cout << "v1的有效长度为:" << v1.size() << endl;cout << "v1的容量大小为:" << v1.capacity() << endl;v1.reserve(10);cout << "reserve(10)后:" << endl;cout << "v1的有效长度为:" << v1.size() << endl;cout << "v1的容量大小为:" << v1.capacity() << endl;cout << endl;v1.resize(8, 10);for (auto& e : v1){cout << e << " ";}cout << endl;cout << "v1的有效长度为:" << v1.size() << endl;cout << "v1的容量大小为:" << v1.capacity() << endl;v1.resize(3);for (auto& e : v1){cout << e << " ";}cout << "v1的有效长度为:" << v1.size() << endl;cout << "v1的容量大小为:" << v1.capacity() << endl;}int main()
{Test06();return 0;
}

3268a39a4ef64316b184f7eed9ff4b9b.png

2.4. vector的访问操作 

 7c91343943e74656b453ac8d519baa86.png

a125abd95aca431599e5f9ec7fbf9629.png

void Test07()
{vector<int> v = { 1,2,3,4,5 };for (int i = 0; i < v.size(); i++){cout << v.at(i) << " ";}cout << endl;for (int i = 0; i < v.size(); i++){cout << v[i] << " ";}cout << endl;cout << "front:" << v.front() << endl;cout << "back:" << v.back() << endl;
}int main()
{Test07();return 0;
}

f8c3d76fef5347e3a9bc1c070ea4ad0c.png

2.5. vector的修改操作 

0fac8b321376493ca30627b92c9e9080.png

d8105e101d1a4b98bb8fcf4b2f9cfc9c.png

首先先介绍最简单的四个函数push_back()pop_back()assign()swap()

void Test7()
{vector<int> v = { 1,2,3,4,5,6 };cout << "back:" << v.back() << endl;//尾插v.push_back(7);//尾删cout << "back:" << v.back() << endl;v.pop_back();cout << "back:" << v.back() << endl;vector<int> vv = { 6,5,4,3,2,1 };//n个val赋值给原数组vv.assign(3, 2);for (int i = 0; i < vv.size(); i++){cout << vv[i] << " ";}cout << endl;vv.swap(v);for (int i = 0; i < v.size(); i++){cout << v[i] << " ";}cout << endl;for (int i = 0; i < vv.size(); i++){cout << vv[i] << " ";}
}

1a2941ef5d104f62b9d3f0903f4be33a.png

介绍insert()与·earse()的用法,这两个函数的用法就与string中的有所不同。首先是insert()函数: 

5995e0b2240946769db167b728c1232f.png

指定位置插入,要注意的是这里不再像string一样,用的size_t 的pos,vector虽然也可以用下标访问,但是为了承接后面STL其他不支持下标访问的容器,所以这边的pos用的是迭代器类型 

void Test8()
{vector<int> myvector(3, 100);vector<int>::iterator it = myvector.begin();//1.向指定位置插入一个元素it = myvector.insert(it, 200);cout << "myvector contains:";for (it = myvector.begin(); it < myvector.end(); it++)cout << ' ' << *it;cout << endl;//2.向指定位置插入n个元素myvector.insert(it, 2, 300);cout << "myvector contains:";for (it = myvector.begin(); it < myvector.end(); it++)cout << ' ' << *it;cout << endl;//3.向指定位置插入一段迭代器区间it = myvector.begin();vector<int> anothervector(2, 400);cout << "myvector contains:";for (it = myvector.begin(); it < myvector.end(); it++)cout << ' ' << *it;cout << endl;it = myvector.begin();myvector.insert(it + 2, anothervector.begin(), anothervector.end());//4.向指定位置插入一段迭代器区间int myarray[] = { 501,502,503 };myvector.insert(myvector.begin(), myarray, myarray + 3);cout << "myvector contains:";for (it = myvector.begin(); it < myvector.end(); it++)cout << ' ' << *it;cout << endl;
}

e8bde24df4f24e38a0ffb45c8fae61e7.png

 

erase()

 

删除单个元素: 当你需要删除向量中的某个特定元素时,可以使用 erase 函数,并传递一个指向该元素的迭代器。这将删除该元素,并使后续元素向前移动一个位置。

删除多个元素: erase 函数还可以接受两个迭代器参数,表示要删除的元素范围(左闭右开区间)。这允许你一次性删除多个连续的元素。

无论是哪种形式,erase 的返回值都是一个迭代器,指向被删除元素之后的位置(即第一个未被删除的元素)。如果删除的是容器中的最后一个元素或所有元素,则返回 vector::end() 迭代器。

void Test9()
{//1.删除迭代器所指元素vector<int> myvector;for (int i = 1; i <= 10; i++) myvector.push_back(i);vector<int>::iterator it = myvector.erase(myvector.begin() + 5);it = myvector.erase(it);//2.删除一段迭代器区间it = myvector.erase(myvector.begin(), myvector.begin() + 3);cout << "myvector contains:";for (int i = 0; i < myvector.size(); ++i)cout << ' ' << myvector[i];cout << endl;
}

71f02f457ced4129891a9cc7506e745f.png

虽然看起来vector的insert()和erase()与string的没有什么区别,但是仔细观察就可以发现我们每次使用完迭代器之后都会更新,这是为什么呢?

主要还是因为我们每次插入数组都可能发生扩容,而扩容分为就地扩容与异地扩容。如果发生的异地扩容,这时的迭代器就不在指向原来的空间,而就指向一块释放的内存,我们一旦继续访问就会报错,这种现象我们称为迭代器失效。为了避免出现这种情况,所以我们在使用完迭代器之后需要更新。

f7c1c8f0879e47fca0352212fa0faecf.png

 

 

 

 

 

 

 

 

 

 

 

 


http://www.mrgr.cn/news/70921.html

相关文章:

  • STM32(hal库)在串口中,USART和uart有什么区别?
  • 20241110,LeetCode 每日一题,用 Go 使用反转整数
  • 项目模块详细说明
  • 基于springboot的家装平台设计与实现
  • Python中4个高效小技巧
  • A算法详解(go实现)
  • 可编辑PPT | 指挥中心系统建设与应用方案
  • c语言数据结构与算法--简单实现栈和队列的出栈与入栈
  • 【快捷入门笔记】mysql基本操作大全-SQL数据库
  • NVR批量管理软件/平台EasyNVR多个NVR同时管理基于端-边-云架构的智能视频融合云平台
  • 通过VirtualBox虚拟机安装和调试编译好的 ReactOS
  • 项目管理全流程平台(源码+文档+部署+讲解)
  • 慌慌张张匆匆忙忙,验证新人如何稳住
  • 光流法与直接法在SLAM中的应用
  • SQL Server Cross Apply 将某列分号的值转换多行
  • Linux文件以及文件夹操作命令 以及Linux文件夹含义最简单易懂的解释 Centos
  • Generator 函数的语法和用法
  • package.json中“type“: “module“是什么含义,es6和commonjs的区别以及require和import使用场景
  • ScheduledThreadPoolExecutor 定制化线程池任务调度底层原理
  • 一文了解什么是CNAPP
  • 京东关键词API接口获取
  • SpringBoot(十一)SpringBoot上传文件
  • 【Linux系统编程】第四十五弹---线程互斥:从问题到解决,深入探索互斥量的原理与实现
  • IOT物联网低代码可视化大屏解决方案汇总
  • runOnUiThread
  • .NET 公共语言运行时(Common Language Runtime,CLR)