C++——vector的了解与使用
目录
引言
vector容器的基本概念
1.功能
2.动态大小
3.动态扩展
vector的接口
1.vector的迭代器
2.vector的初始化与销毁
3.vector的容量操作
3.1 有效长度和容量大小
(1)使用示例
(2)扩容机制
3.2 有效长度和容量操作
(1)reserve
(2)resize
4.vector的访问操作
5.vector的修改操作
迭代器失效
结束语
引言
在我的博客 C++——string的了解和使用 中,我们学习了标准模板库(STL)中的string的一些基础内容,今天我们来学习 vector 。
vector容器的基本概念
在C++中,vector 是一个非常重要的容器,它属于标准模板库的一部分。vector 提供了一种动态数组的实现,可以在运行时动态地增加或减少其大小,同时保持元素的连续存储。
1.功能
vector是可变大小的序列容器,采用连续存储空间存储元素,可通过下标高效访问。
2.动态大小
与静态数组不同,vector 的大小可以在运行时动态地改变。此外,静态数组内数据通常存储在栈上,而vector中数据存储在堆上。
3.动态扩展
动态扩展的核心在于,当现有内存空间不足以满足需求时,不是简单地在原空间后续接新空间(这在连续内存分配中通常是不现实的,因为后续空间可能已被其他程序或数据占用),而是采取以下步骤:
(1)分配新空间:找到一个足够大的、连续的新内存空间。这个新空间的大小要能够容纳原数据和新数据。
(2)数据拷贝:将原内存空间中的数据复制到新分配的内存空间中。这个过程中需要确保数据的完整性和正确性。
(3)释放原空间:在数据成功拷贝到新空间后,释放原来的内存空间。这一步是为了避免内存泄漏。
(4)更新指针:将指向原内存空间的指针更新为指向新内存空间的指针。这样,程序就可以继续在新空间中操作数据。
此外,使用vector时必须包含头文件 #include<vector> 。
vector的接口
我们使用如下文档来辅助我们学习vector:
vector的接口详细介绍
1.vector的迭代器
与string一样,vector中也有迭代器。由于vector定义在vector类中,因此我们想要使用vector的迭代器需要通过域作用限定符访问——vector<类型>::iterator 。
其中,begin(),end(),rbeign(),rend()的使用访问方法与string中的类似,
我们来看看代码演示:
int main()
{vector<int> vec = { 0,1,2,3,4,5,6,7,8,9 };vector<int>::iterator it = vec.begin();cout << "顺序遍历:";while (it != vec.end()){cout << *it << " ";it++;}cout << endl;cout << "逆序遍历:";vector<int>::reverse_iterator rit = vec.rbegin();while (rit != vec.rend()){cout << *rit << " ";++rit;}return 0;
}
输出结果如下:
同样的,vector也支持const_iterator:
int main()
{vector<int> vec = { 0,1,2,3,4,5,6,7,8,9 };for (vector<int>::const_iterator it = vec.begin(); it != vec.end(); ++it) {std::cout << *it << " ";}std::cout << std::endl;return 0;
}
2.vector的初始化与销毁
vector 类的赋值运算符 operator= 被重载以支持将一个 vector 的内容复制到另一个 vector 中。
vector 支持多种构造函数,拷贝构造以及赋值运算符重载。
下面是一些简单的使用:
void printVector(vector<int>& vec)
{ for (vector<int>::iterator it = vec.begin(); it != vec.end(); ++it){cout << *it << " ";}cout << endl;
}int main()
{vector<int> vec1; for (int i = 0; i < 5; ++i){vec1.push_back(i);}vector<int> vec2(vec1.begin(), vec1.end()); vector<int> vec3(5, 5); vector<int> vec4(vec3); cout << "打印vec2: ";printVector(vec2);cout << "打印vec3: ";printVector(vec3);cout << "打印vec4: ";printVector(vec4);return 0;
}
输出结果为:
int main()
{vector<int> vec1, vec2;for (int i = 0; i < 5; i++){vec1.push_back(i);}vec2 = vec1;printVector(vec2);return 0;
}
输出结果为:
3.vector的容量操作
如下表格是关于vector的一些基础的容量操作:
函数名称 | 功能 |
size | 返回vector的有效长度 |
capacity | 返回vector的容量大小 |
clear | 清空vector |
empty | 检查vector是否为空,是则返回ture,否则返回false |
reserve | 请求改变vector的容量 |
resize | 重新设置有效元素的数量,超过原来有效长度则用c字符填充 |
3.1 有效长度和容量大小
(1)使用示例
与 string类 类似,我们可以使用 size()返回容器的有效长度;capacity()返回容器的容量大小。
下面是简单的测试用例:
int main()
{vector<int> vec = { 0,1,2,3,4,5,6,7,8,9 };cout << vec.size() << endl;cout << vec.capacity() << endl;if (vec.empty()){cout << "vec为空" << endl;}else{cout << "vec不为空" << endl;}vec.clear();if (vec.empty()){cout << "vec为空" << endl;}else{cout << "vec不为空" << endl;}return 0;
}
输出结果为:
通常来说,vector的有效长度和容量大小相同
(2)扩容机制
接下来我们来看看vector的扩容机制:
int main()
{size_t sz;vector<int> vec;sz = vec.capacity();cout << "making vec grow:" << endl;for (int i = 0; i < 100; ++i){vec.push_back(i);if (sz != vec.capacity()){sz = vec.capacity();cout << "capacity changed: " << sz << endl;}}
}
输出结果为:
在vs2022中,vector是以1.5倍进行扩容处理的,
我们现在把这段代码放到g++中运行看看:
我们可以看到,在此环境中,vector是以二倍进行扩容的。
3.2 有效长度和容量操作
接下来我们来学习vector中的reserve和resize(这俩函数的使用与string中的差不多)。
(1)reserve
reserve 函数用于请求改变容器的容量,即分配足够的内存空间以容纳至少指定数量的元素
简单使用一下:
int main()
{vector<int> vec;vec.reserve(10);cout << vec.capacity() << endl;return 0;
}
输出结果为:
(2)resize
resize的使用有如下两种:
resize(int num);
如果 num 小于当前容器的大小,那么容器末尾的多余元素将被删除。如果 num 大于当前容器的大小,则容器将被扩展,新添加的元素将被初始化为该类型的默认值(对于内置类型如 int,默认值为 0)。
resize(int num, int elem);
这个方法同样用于将 std::vector 的大小调整为 num,但在容器需要扩展时,新添加的元素将被初始化为 elem 而不是默认值。如果 num 小于当前容器的大小,多余元素的处理方式与第一个 resize 方法相同,即被删除。
简单的使用一下:
int main()
{vector<int> vec = { 1,2,3,4,5 };vec.resize(3);for (int i = 0; i < vec.size(); i++){cout << vec[i] << " ";}cout << endl;vec.resize(4);for (int i = 0; i < vec.size(); i++){cout << vec[i] << " ";}cout << endl;vec.resize(5, 9);for (int i = 0; i < vec.size(); i++){cout << vec[i] << " ";}cout << endl;return 0;
}
输出结果为:
4.vector的访问操作
vector的访问操作有如下几个:
函数名称 | 功能 |
operator[] | 返回指定位置的元素,越界则报错 |
at | 返回指定位置的元素,越界则抛异常 |
front | 返回字符串第一个元素 |
back | 返回字符串最后一个元素 |
这些函数的用法也与string中的函数用法差不多,就不多介绍了,来看看代码:
int main()
{vector<int> vec = { 0,1,2,3,4,5,6,7,8,9 };for (int i = 0; i < vec.size(); i++){cout << vec[i] << " ";}cout << endl;for (int i = 0; i < vec.size(); i++){cout << vec.at(i) << " ";}cout << endl;cout << "front:" << vec.front() << endl;cout << "back:" << vec.back() << endl;return 0;
}
输出结果为:
5.vector的修改操作
vector的修改操作有如下几个:
函数名称 | 功能 |
push_back | 在数组后追加元素 |
pop_back | 删除数组最后一个元素 |
insert | 在指定位置追加元素 |
assign | 使用指定数组替换原数组 |
erase | 删除数组指定部分区间 |
swap | 交换两个数组 |
先来看看push_back(),pop_back(),assign(),swap() :
int main()
{vector<int> vec1 = { 0,1,2,3,4,5,6,7,8,9 };cout << "back:" << vec1.back() << endl;// 元素尾插vec1.push_back(10);// 元素尾删cout << "back:" << vec1.back() << endl;vec1.pop_back();cout << "back:" << vec1.back() << endl;vector<int> vec2 = { 9,9,9,9,9 };vec2.assign(3, 3);for (int i = 0; i < vec2.size(); i++){cout << vec2[i] << " ";}cout << endl;vec2.swap(vec1);for (int i = 0; i < vec1.size(); i++){cout << vec1[i] << " ";}cout << endl;for (int i = 0; i < vec2.size(); i++){cout << vec2[i] << " ";}cout << endl;return 0;
}
输出结果如下:
我们再来看看insert和erase:
insert函数:
来看代码:
int main()
{vector<int> vec1(5, 10);vector<int>::iterator it = vec1.begin();it = vec1.insert(it, 100);cout << "vec1:";for (it = vec1.begin(); it < vec1.end(); it++){cout << *it << " ";}cout << endl;vec1.insert(it, 2, 1000);it = vec1.begin();cout << "vec1:";for (it = vec1.begin(); it < vec1.end(); it++){cout << *it << " ";}cout << endl;vector<int> vec2(2, 10000);it = vec1.begin();vec1.insert(it + 2, vec2.begin(), vec2.end());cout << "vec1:";for (it = vec1.begin(); it < vec1.end(); it++){cout << *it << " ";}cout << endl;return 0;
}
输出结果为:
erase函数:
简单的代码演示如下:
int main()
{vector<int> vec = { 0,1,2,3,4,5,6,7,8,9 };vector<int>::iterator it = vec.erase(vec.begin() + 3); // 删除值为3的元素it = vec.erase(it); // 删除值为4的元素for (int i = 0; i < vec.size(); i++){cout << vec[i] << " ";}cout << endl;it = vec.erase(vec.begin(), vec.begin() + 5);for (int i = 0; i < vec.size(); i++){cout << vec[i] << " ";}cout << endl;return 0;
}
输出结果为:
迭代器失效
迭代器的主要作用就是让算法能够不用关心底层数据结构,其底层实际就是一个指针,或者是对 指针进行了封装,比如:vector的迭代器就是原生态指针T* 。因此迭代器失效,实际就是迭代器 底层对应指针所指向的空间被销毁了,而使用一块已经被释放的空间,造成的后果是程序崩溃(即 如果继续使用已经失效的迭代器,程序可能会崩溃)。
以下可能会导致迭代器失效:
1.会引起其底层空间改变的操作,都有可能使迭代器失效
int main()
{vector<int> v{ 1,2,3,4,5,6 };auto it = v.begin();// 将有效元素个数增加到100个,多出的位置使用8填充,操作期间底层会扩容// v.resize(100, 8);// reserve的作用就是改变扩容大小但不改变有效元素个数,操作期间可能会引起底层容量改变// v.reserve(100);// 插入元素期间,可能会引起扩容,而导致原空间被释放// v.insert(v.begin(), 0);// v.push_back(8);// 给vector重新赋值,可能会引起底层容量改变//v.assign(100, 8);/*出错原因:以上操作,都有可能会导致vector扩容,也就是说vector底层原理旧空间被释放掉,而在打印时,it还使用的是释放之间的旧空间,在对it迭代器操作时,实际操作的是一块已经被释放的空间,而引起代码运行时崩溃。解决方式:在以上操作完成之后,如果想要继续通过迭代器操作vector中的元素,只需给it重新赋值即可。 */while (it != v.end()){cout << *it << " ";++it;}cout << endl;return 0;
}
2.指定位置元素的删除操作--erase
int main()
{int a[] = { 1, 2, 3, 4 };vector<int> v(a, a + sizeof(a) / sizeof(int));// 使用find查找3所在位置的iteratorvector<int>::iterator pos = find(v.begin(), v.end(), 3);// 删除pos位置的数据,导致pos迭代器失效。v.erase(pos);cout << *pos << endl; // 此处会导致非法访问return 0;
}
3.与vector一样,string在插入+扩容操作+erase之后,迭代器也会失效
int main()
{string s("hello");auto it = s.begin();// 放开之后代码会崩溃,因为resize到20会string会进行扩容// 扩容之后,it指向之前旧空间已经被释放了,该迭代器就失效了// 后序打印时,再访问it指向的空间程序就会崩溃//s.resize(20, '!');while (it != s.end()){cout << *it;++it;}cout << endl;it = s.begin();while (it != s.end()){it = s.erase(it);// 按照下面方式写,运行时程序会崩溃,因为erase(it)之后// it位置的迭代器就失效了// s.erase(it); ++it;}return 0;
}
结束语
写完啦~\(≧▽≦)/~
接下来会愈加忙碌,希望自己还能挤出时间写博客=_=
感谢各位大佬的支持!!!
求点赞收藏评论关注!!!
相关文章:

C++——vector的了解与使用
目录 引言 vector容器的基本概念 1.功能 2.动态大小 3.动态扩展 vector的接口 1.vector的迭代器 2.vector的初始化与销毁 3.vector的容量操作 3.1 有效长度和容量大小 (1)使用示例 (2)扩容机制 3.2 有效长度和容量操作 (1)reserve (2)resize 4.vector的访问操作…...
Ubuntu设置静态IP地址
Ubuntu如果是最小安装,没有图形界面,需要配置静态IP,该怎么操作呢? Netplan 是最新版 Ubuntu 的默认网络管理工具。Netplan 的配置文件使用 YAML 编写,扩展名为 .yaml。 注意:配置文件中的空格是语法的一部…...
力扣349.两个数组的交集
题目链接:349. 两个数组的交集 - 力扣(LeetCode) 给定两个数组 nums1 和 nums2 ,返回 它们的 交集。输出结果中的每个元素一定是 唯一 的。我们可以 不考虑输出结果的顺序 。 示例 1: 输入:nums1 [1,2,…...

FreeRTOS - 软件定时器
在学习FreeRTOS过程中,结合韦东山-FreeRTOS手册和视频、野火-FreeRTOS内核实现与应用开发、及网上查找的其他资源,整理了该篇文章。如有内容理解不正确之处,欢迎大家指出,共同进步。 1. 软件定时器 软件定时器也可以完成两类事情…...
Python的Atlassian第三方库的详细介绍
atlassian-python-api 是一个用于与 Atlassian 生态系统进行交互的 Python 库,支持与多种 Atlassian 工具(如 Jira、Confluence、Bitbucket 等)进行 API 调用。它简化了 REST API 的调用,提供了高层次的抽象,方便开发者…...
Java中的基本循环结构详解
在Java编程中,循环是控制流的重要组成部分,用于重复执行一段代码。Java提供了三种基本的循环结构:for循环、while循环和do-while循环。本文将详细介绍这三种循环的语法和使用场景,并通过示例代码展示其应用。 一,for …...

关于Git Bash中如何定义alias
一、在一次临时Bash会话中使用alias 在Bash中直接输入alias xxdddd,xx为对应要执行的命令的缩写,dddd为要执行的命令,如alias ddcd /d,输入完成后,在Bash中输入dd,即可切换至D盘。 此种设置方式ÿ…...

luckfox1106初次使用
luckfox1106初次使用 下载rk驱动 https://files.luckfox.com/wiki/Luckfox-Pico/Software/DriverAssitant_v5.12.zip 安装驱动 SD 卡烧录工具 https://files.luckfox.com/wiki/Luckfox-Pico/Software/SocToolKit_v1.98_20240705_01_win.zip 右键以管理员方式运行...
ab命令深入解析:ApacheBench性能测试工具
软考鸭微信小程序 学软考,来软考鸭! 提供软考免费软考讲解视频、题库、软考试题、软考模考、软考查分、软考咨询等服务 引言 在Web开发和运维领域,性能测试是评估服务器和应用性能的重要手段。ApacheBench(简称ab)是Apache HTTP服务器自带的…...

VSCode创建VUE项目(二)前端登录页面
一.创建登录页面 代码: <template><div class"login-container dis-h"><div class"login-form dis-h"><div class"dis-v left"><span> 欢迎~ </span><span> VUE 新世界 </span>&l…...

centos 8.4学习小结
1.权限委派 2.vim快捷方式 2.1非正常关闭文本处理方式 2.2快捷方式 2.3TAB键补齐安装包 [ rootcloud Packages]# rpm -ivh bash-completion-2.7-5.el8.noarch.rpm 2.4#history 查询历史记录 [rootcloud ~]# vim /etc/profile HISTSIZE1000(默认保存1000条历史记…...

AI 设计工具合集
🐣个人主页 可惜已不在 🐤这篇在这个专栏AI_可惜已不在的博客-CSDN博客 🐥有用的话就留下一个三连吧😼 前言: AI 视频,科技与艺术的精彩融合。它借助先进的人工智能技术,为影像创作带来全新可能。本书…...
mac 源代码安装openresty
1. clone源代码 git clone https://github.com/openresty/openresty.git 2. 安装依赖包 brew install hg unix2dos brew install pcre openssl 3. 编译 make 4. 安装openresty cd openresty-1.27.1.1 ./configure --prefix/opt/openresty --with-http_ssl_module --with-…...

人工智能和机器学习之线性代数(二)
人工智能和机器学习之线性代数(二) 本文Linear Algebra 101 for AI/ML – Part 2将通过介绍向量的点积(dot Product)、Embedding及其在相似性搜索中的应用来建立这些基础知识。 将学习Embedding,Embedding是表示概念、对象和想法的特殊类型的向量。Embedding在整个…...
Postman中的form-data 和 JSON 的区别
在使用 Postman 进行 API 测试时,form-data 和 JSON 是两种常用的请求体格式,它们有以下几个主要区别: 1. 数据格式 form-data: 主要用于表单数据的提交,适合文件上传和键值对的数据传递。数据以键值对的形式编码,类似…...

网络安全基础知识点_网络安全知识基础知识篇
文章目录 一、网络安全概述1.1 定义1.2 信息安全特性1.3 网络安全的威胁1.4 网络安全的特征 二、入侵方式2.1 黑客2.1.1 入侵方法2.1.2 系统的威胁2.2 IP欺骗与防范2.2.1 TCP等IP欺骗基础知识2.2.2 IP欺骗可行的原因2.2.3 IP欺骗过程2.2.4 IP欺骗原理2.2.5 IP欺骗防范2.3 Sniff…...
Vue.js 从入门到精通:全面解析组件化、路由与状态管理(附 Todo 案例)
在当今的前端开发领域,Vue.js 以其简洁、高效和灵活的特点受到了广泛的关注和应用。本文将带你从 Vue 的基础知识入手,逐步深入到高级特性,让你对 Vue 有一个全面的了解,并通过实际案例帮助你更好地掌握 Vue 的开发。 一、Vue 简…...

AI Weekly#1:过去一周重要的AI资讯汇总
🚀热点头条 诺贝尔奖青睐AI领域:2024年诺贝尔物理学奖和化学奖均授予了与人工智能相关的研究。物理学奖颁发给了约翰霍普菲尔德和杰弗里辛顿,表彰他们在机器学习领域的开创性工作。化学奖则授予了大卫贝克、德米斯哈萨比斯和约翰江珀…...
图论刷题
卡码网 98. 所有可达路径 使用邻接矩阵存储: #include<iostream> #include<vector> using namespace std;vector<vector<int>>res;//收集符合条件的路径vector<int>path;//0节点到终点的路径//确定递归函数 参数和返回值void dfs(c…...
ICM20948 DMP代码详解(85)
接前一篇文章:ICM20948 DMP代码详解(84) 上一回解析了inv_icm20948_ctrl_enable_sensor函数的大部分代码,只剩下一行代码没有解析。为了便于理解和回顾,再次贴出inv_icm20948_ctrl_enable_sensor函数源码,在EMD-Core\sources\Invn\Devices\Drivers\ICM20948\Icm20948Data…...

Unity3D中Gfx.WaitForPresent优化方案
前言 在Unity中,Gfx.WaitForPresent占用CPU过高通常表示主线程在等待GPU完成渲染(即CPU被阻塞),这表明存在GPU瓶颈或垂直同步/帧率设置问题。以下是系统的优化方案: 对惹,这里有一个游戏开发交流小组&…...
DockerHub与私有镜像仓库在容器化中的应用与管理
哈喽,大家好,我是左手python! Docker Hub的应用与管理 Docker Hub的基本概念与使用方法 Docker Hub是Docker官方提供的一个公共镜像仓库,用户可以在其中找到各种操作系统、软件和应用的镜像。开发者可以通过Docker Hub轻松获取所…...

视频行为标注工具BehaviLabel(源码+使用介绍+Windows.Exe版本)
前言: 最近在做行为检测相关的模型,用的是时空图卷积网络(STGCN),但原有kinetic-400数据集数据质量较低,需要进行细粒度的标注,同时粗略搜了下已有开源工具基本都集中于图像分割这块,…...

七、数据库的完整性
七、数据库的完整性 主要内容 7.1 数据库的完整性概述 7.2 实体完整性 7.3 参照完整性 7.4 用户定义的完整性 7.5 触发器 7.6 SQL Server中数据库完整性的实现 7.7 小结 7.1 数据库的完整性概述 数据库完整性的含义 正确性 指数据的合法性 有效性 指数据是否属于所定…...

[大语言模型]在个人电脑上部署ollama 并进行管理,最后配置AI程序开发助手.
ollama官网: 下载 https://ollama.com/ 安装 查看可以使用的模型 https://ollama.com/search 例如 https://ollama.com/library/deepseek-r1/tags # deepseek-r1:7bollama pull deepseek-r1:7b改token数量为409622 16384 ollama命令说明 ollama serve #:…...

【从零开始学习JVM | 第四篇】类加载器和双亲委派机制(高频面试题)
前言: 双亲委派机制对于面试这块来说非常重要,在实际开发中也是经常遇见需要打破双亲委派的需求,今天我们一起来探索一下什么是双亲委派机制,在此之前我们先介绍一下类的加载器。 目录 编辑 前言: 类加载器 1. …...

Chrome 浏览器前端与客户端双向通信实战
Chrome 前端(即页面 JS / Web UI)与客户端(C 后端)的交互机制,是 Chromium 架构中非常核心的一环。下面我将按常见场景,从通道、流程、技术栈几个角度做一套完整的分析,特别适合你这种在分析和改…...
【WebSocket】SpringBoot项目中使用WebSocket
1. 导入坐标 如果springboot父工程没有加入websocket的起步依赖,添加它的坐标的时候需要带上版本号。 <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-websocket</artifactId> </dep…...

Redis上篇--知识点总结
Redis上篇–解析 本文大部分知识整理自网上,在正文结束后都会附上参考地址。如果想要深入或者详细学习可以通过文末链接跳转学习。 1. 基本介绍 Redis 是一个开源的、高性能的 内存键值数据库,Redis 的键值对中的 key 就是字符串对象,而 val…...
C++ 类基础:封装、继承、多态与多线程模板实现
前言 C 是一门强大的面向对象编程语言,而类(Class)作为其核心特性之一,是理解和使用 C 的关键。本文将深入探讨 C 类的基本特性,包括封装、继承和多态,同时讨论类中的权限控制,并展示如何使用类…...