【C++】STL----vector常见用法

news2024/11/15 14:06:31

🔥个人主页🔥:孤寂大仙V
🌈收录专栏🌈:C++从小白到高手
🌹往期回顾🌹:[C++]string类
🔖 流水不争,争的是滔滔不息。

文章目录

  • 一、vector的介绍
    • vector的主要特点
  • 二、vector的使用
    • vector的定义
    • vector迭代器的使用
    • vector的空间增长问题
    • vector的增删查改
  • 三、vector迭代器失效问题
    • 对于vector可能导致迭代器失效的原因


一、vector的介绍

在C++中,vector是标准模板库(STL)中的一种动态数组容器,它允许存储同一类型的元素,并能自动调整大小。这与普通数组不同,vector的大小是可以动态改变的,即可以根据需要自动扩展或缩小。

vector的主要特点

  1. 动态大小:不像数组需要固定的大小,vector可以动态增加或减少元素个数。当新元素被添加时,它会自动调整底层存储容量。
  2. 顺序容器:vector中的元素按插入顺序存储,支持通过下标访问和修改元素,类似于数组。
  3. 自动管理内存:vector会自动管理内存分配和释放,因此用户不必担心手动分配内存,避免了使用普通数组时可能出现的内存泄漏问题。
  4. 灵活的插入和删除操作:vector可以在末尾进行高效的插入和删除操作。虽然在中间插入和删除元素的效率较低,但相比普通数组仍有较多的灵活性。

二、vector的使用

vector的定义

构造函数声明
在这里插入图片描述
在这里插入图片描述

vector<int> v1;//无参构造

在这里插入图片描述

vector<int> v2(10,1);//构造并初始化10个1

在这里插入图片描述

vector<int> v3(10, 2);
vector<int> v4 = v3;//拷贝构造

在这里插入图片描述

vector<int> v4(10, 3);
vector<int> v5(v4.begin(), v4.end());
paint_vector(v5);//使用迭代器进行初始化构造

在这里插入图片描述

vector迭代器的使用

在这里插入图片描述
在这里插入图片描述

void paint_vector2(vector<int>& v)
{
	auto it = v.begin();
	while (it != v.end())
	{
		cout << *it << " ";
		++it;
	}
	cout << endl;
}
void test5()
{
	vector<int> v5;
	v5.push_back(1);
	v5.push_back(2);
	v5.push_back(3);
	v5.push_back(4);
	v5.push_back(5);
	paint_vector2(v5);
}
int main()
{
	test5();
	return 0;
}

用vector创建一个容器,在里面放五个整形元素。打印的话用迭代器打印。begin()是获取第一个位置的数据元素,end()是获取最后一个位置的下一个位置的数据元素。
在这里插入图片描述

void paint_vector3(vector<int>& v)
{
	auto it = v.rbegin();
	while (it != v.rend())
	{
		cout << *it << " ";
		++it;
	}
	cout << endl;
}
void test6()
{
	vector<int> v6;
	v6.push_back(1);
	v6.push_back(2);
	v6.push_back(3);
	v6.push_back(4);
	v6.push_back(5);
	paint_vector3(v6);
}
int main()
{
	test6();
	return 0;
}

rbegin正好反了过来,rbegin获取最后一个位置,rend获取第一个位置的前一个位置。
在这里插入图片描述

vector的空间增长问题

在这里插入图片描述
在这里插入图片描述

vector<int> v7(10, 7);
cout << size(v7) << endl;//size获取数据个数

在这里插入图片描述

vector<int> v8(10, 8);
cout << v8.capacity() << endl;//获取容量大小

在这里插入图片描述

vector<int> v10(10, 10);
v10.resize(15, 5);//改变vector的size并且可以为增加的size的改变值	
paint_vector(v10);

在这里插入图片描述

vector<int> v11(10, 11);
v11.reserve(15);//一开始的capacity是10,用reserve改变capacity的容量

一开始的的capacity是10
在这里插入图片描述
经过reserve改变容量后capacity变为了15
在这里插入图片描述
capacity的代码在vs和g++下分别运行会发现,vs下capacity是按1.5倍增长的,g++是按2
倍增长的。这个问题经常会考察,不要固化的认为,vector增容都是2倍,具体增长多少是
根据具体的需求定义的。vs是PJ版本STL,g++是SGI版本STL。
reserve只负责开辟空间,如果确定知道需要用多少空间,reserve可以缓解vector增容的代
价缺陷问题。
resize在开空间的同时还会进行初始化,影响size。

vector的增删查改

在这里插入图片描述
在这里插入图片描述

vector<int> v12;
v12.push_back(1);
v12.push_back(2);//尾插
paint_vector(v12);

在这里插入图片描述

vector<int> v13(10,1);
v13.pop_back();
v13.pop_back();//尾删
paint_vector(v13);

在这里插入图片描述

void test14()
{
	vector<int> v14;
	v14.push_back(1);
	v14.push_back(2);
	v14.push_back(3);
	v14.push_back(4);
	v14.push_back(5);
	v14.push_back(6);
	
	auto it = find(v14.begin(), v14.end(), 5);
	if (it != v14.end())
	{
		cout << *it << " ";
		++it;
	}
	else
	{
		cout << "no" << " ";
	}

}
int main()
{
	test14();
	return 0;
}

如果在vector中可以找到val,就返回我们要找的值,如果vector中没有val,返回no。
在这里插入图片描述

void test15()
{
	vector<int> v15(10,1);
	v15.insert(v15.begin()+3, 2);//在pos位置之前插入val。
	paint_vector(v15);
}
int main()
{
	test15();
	return 0;
}

在这里插入图片描述

void test16()
{
	vector<int> v16;
	v16.push_back(1);
	v16.push_back(2);
	v16.push_back(3);
	v16.push_back(4);
	v16.push_back(5);

	v16.erase(v16.begin() + 3);//删除pos位置的数据
	paint_vector(v16);
}
int main()
{
	test16();
	return 0;
}

在这里插入图片描述

void test17()
{
	vector<int> v17(5, 3);
	vector<int> v18(5, 6);

	paint_vector(v17);
	paint_vector(v18);
	swap(v17, v18);//交换两个vetor的数据空间。
	paint_vector(v18);
	paint_vector(v17);
}

int main()
{
	test17();
	return 0;
}

在这里插入图片描述

void test18()
{
	vector<int> v18;
	v18.push_back(1);
	v18.push_back(2);
	v18.push_back(3);
	v18.push_back(4);
	v18.push_back(5);

	cout<<v18[1]<<endl;
}
int main()
{
	test18();
	return 0;
}

在这里插入图片描述

三、vector迭代器失效问题

迭代器的主要作用就是让算法能够不用关心底层数据结构,其底层实际就是一个指针,或者是对
指针进行了封装,比如:vector的迭代器就是原生态指针T* 。因此迭代器失效,实际就是迭代器
底层对应指针所指向的空间被销毁了,而使用一块已经被释放的空间,造成的后果是程序崩溃(即
如果继续使用已经失效的迭代器,程序可能会崩溃)。

对于vector可能导致迭代器失效的原因

1.会引起其底层空间改变的操作,都有可能是迭代器失效,比如:resize、reserve、insert、
assign、push_back等。

#include <iostream>
 using namespace std;
 #include <vector>
 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;
 }
  1. . 指定位置元素的删除操作–erase
#include <iostream>
using namespace std;
 #include <vector>
 int main()
 {
 int a[] = { 1, 2, 3, 4 };
 vector<int> v(a, a + sizeof(a) / sizeof(int));
 // 使用find查找3所在位置的iterator
 vector<int>::iterator pos = find(v.begin(), v.end(), 3);
 // 删除pos位置的数据,导致pos迭代器失效。
v.erase(pos);
 cout << *pos << endl; // 此处会导致非法访问
return 0;
 }

erase删除pos位置元素后,pos位置之后的元素会往前搬移,没有导致底层空间的改变,理
论上讲迭代器不应该会失效,但是:如果pos刚好是最后一个元素,删完之后pos刚好是end
的位置,而end位置是没有元素的,那么pos就失效了。因此删除vector中任意位置上元素
时,vs就认为该位置迭代器失效了。

  1. 注意:Linux下,g++编译器对迭代器失效的检测并不是非常严格,处理也没有vs下极端。
// 1. 扩容之后,迭代器已经失效了,程序虽然可以运行,但是运行结果已经不对了
int main()
 {
 vector<int> v{1,2,3,4,5};
 for(size_t i = 0; i < v.size(); ++i)
 cout << v[i] << " ";
 cout << endl;
 auto it = v.begin();
 cout << "扩容之前,vector的容量为: " << v.capacity() << endl;
 // 通过reserve将底层空间设置为100,目的是为了让vector的迭代器失效    
v.reserve(100);
 cout << "扩容之后,vector的容量为: " << v.capacity() << endl;
 // 经过上述reserve之后,it迭代器肯定会失效,在vs下程序就直接崩溃了,但是linux
下不会
// 虽然可能运行,但是输出的结果是不对的
while(it != v.end())
 {
 cout << *it << " ";
 ++it;
 }
 cout << endl;
 return 0;
 }
程序输出:
1 2 3 4 5 
扩容之前,vector的容量为: 5
扩容之后,vector的容量为: 100
 0 2 3 4 5 409 1 2 3 4 5
 // 2. erase删除任意位置代码后,linux下迭代器并没有失效
// 因为空间还是原来的空间,后序元素往前搬移了,it的位置还是有效的
#include <vector>
 #include <algorithm>
 int main()
 {
 vector<int> v{1,2,3,4,5};
 vector<int>::iterator it = find(v.begin(), v.end(), 3);
 v.erase(it);
 cout << *it << endl;
 while(it != v.end())
 {
 cout << *it << " ";
 ++it;
 }
 cout << endl;
 return 0;
 }
程序可以正常运行,并打印:
4
 4 5
// 3: erase删除的迭代器如果是最后一个元素,删除之后it已经超过end
 // 此时迭代器是无效的,++it导致程序崩溃
int main()
 {
 vector<int> v{1,2,3,4,5};
 // vector<int> v{1,2,3,4,5,6};
 auto it = v.begin();
 while(it != v.end())
 {
 if(*it % 2 == 0)
 v.erase(it);
 ++it;
 }
 for(auto e : v)
 cout << e << " ";
 cout << endl;
 return 0;
 }
 ========================================================
 // 使用第一组数据时,程序可以运行
[sly@VM-0-3-centos 20220114]$ g++ testVector.cpp -std=c++11
 [sly@VM-0-3-centos 20220114]$ ./a.out
 1 3 5 
=========================================================
 // 使用第二组数据时,程序最终会崩溃
[sly@VM-0-3-centos 20220114]$ vim testVector.cpp 
[sly@VM-0-3-centos 20220114]$ g++ testVector.cpp -std=c++11
 [sly@VM-0-3-centos 20220114]$ ./a.out
 Segmentation fault

从上述三个例子中可以看到:SGI STL中,迭代器失效后,代码并不一定会崩溃,但是运行
结果肯定不对,如果it不在begin和end范围内,肯定会崩溃的。

  1. 与vector类似,string在插入+扩容操作+erase之后,迭代器也会失效
#include <string>
 void TestString()
 {
 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;
    }
 }

迭代器失效解决办法:在使用前,对迭代器重新赋值即可。

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

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

相关文章

MATLAB绘图基础8:双变量图形绘制

参考书&#xff1a;《 M A T L A B {\rm MATLAB} MATLAB与学术图表绘制》(关东升)。 8.双变量图形绘制 8.1 散点图 散点图用于显示两个变量间的关系&#xff0c;每个数据点在图上表示为一个点&#xff0c;一个变量在 X {\rm X} X轴&#xff0c;一个变量在 Y {\rm Y} Y轴&#…

【Python报错已解决】AttributeError: ‘DataFrame‘ object has no attribute ‘append‘

&#x1f3ac; 鸽芷咕&#xff1a;个人主页 &#x1f525; 个人专栏: 《C干货基地》《粉丝福利》 ⛺️生活的理想&#xff0c;就是为了理想的生活! 专栏介绍 在软件开发和日常使用中&#xff0c;BUG是不可避免的。本专栏致力于为广大开发者和技术爱好者提供一个关于BUG解决的经…

暴雨信息|《2024 年全球人工智能趋势报告》发布,GPU仍是AI发展最大关键之一

全球著名调研机构WEKA近日正式发布《2024年全球人工智能趋势报告》&#xff0c;该报告是基于全球1500人工智能决策者的深度调查&#xff0c;覆盖金融、政府、医疗保健等多个行业&#xff0c;解锁了人工智能领域的关键见解和策略。 人工智能正成为许多组织战略的一个基本面&…

滑动窗口(8)_最小覆盖字串

个人主页&#xff1a;C忠实粉丝 欢迎 点赞&#x1f44d; 收藏✨ 留言✉ 加关注&#x1f493;本文由 C忠实粉丝 原创 滑动窗口(8)_最小覆盖字串 收录于专栏【经典算法练习】 本专栏旨在分享学习算法的一点学习笔记&#xff0c;欢迎大家在评论区交流讨论&#x1f48c; 目录 1. 题…

6.C++程序中的基本数据类型

数据类型是指在C中用于声明不同类型变量或函数的一个系统或抽象或者是一个分类&#xff0c;它决定了变量存储占用的内存空间以及解析存储的位模式。其实数据类型可以理解为固定内存大小的别名&#xff0c;是创建变量的模具&#xff0c;具体使用哪种模具&#xff08;包括自定义&…

基于深度学习的文本情感原因提取研究综述——论文阅读

前言 既然要学习情感分析&#xff0c;那么肯定还要了解情感原因对抽取的发展历程&#xff0c;所以我又搜了一篇研究综述&#xff0c;虽然是2023年发表的&#xff0c;但是里面提及到的历程仅停留到2022年。这篇综述发布在TASLP期刊&#xff0c;是音频、声学、语言信号处理的顶级…

进程间的通信-信号量

信号量 1.资源竞争 资源竞争 : 当多个进程同时访问共享资源时&#xff0c;会产生资源竞争&#xff0c;最终最导致数据混乱临界资源 : 不允许同时有多个进程访问的资源&#xff0c;包括硬件资源(CPU、内存、存储器以及其他外围设备)与软件资源(共享代码段、共享数据结构)临界区…

有关JS下隐藏的敏感信息

免责声明&#xff1a;本文仅做分享&#xff01; 目录 JavaScript 介绍 核心组成 工具 FindSomething ** 浏览器检查 ** LinkFinder URLfinder ** SuperSearchPlus ** ffuf ParasCollector waymore Packer Fuzzer JS逆向 应用&#xff1a; 小结&#xff1a; Ja…

基于Python+SQLite的课程管理系统

系统需求简介 1.1需求分析 实现一个具体的课程管理系统。按照软件工程思路设计简化的专业课数据库&#xff0c;尽量模拟现有专业课程一个学期的选课排课原型实际情况。&#xff08;注&#xff1a;本系统由本人单独设计、开发完成&#xff09; 1.2 数据结构需求分析 课程管理…

NAND闪存:迎来新的发展机遇

2024年&#xff0c;存储市场正在经历着动态的变化&#xff0c;其中包括合同价格的上涨、制造商营收的增长以及多项技术上的突破。在这个背景下&#xff0c;主要的存储公司正在为新的挑战做准备&#xff0c;尤其是在NAND闪存领域即将面临转型的情况下。 扩展阅读&#xff1a;20…

PMData:人工智能之运动记录数据集

简介 在这个数据集中&#xff0c;我们展示了 PMData 数据集&#xff0c;旨在将传统的生活记录与体育活动记录相结合。这样的数据集可以开发几个有趣的分析应用程序&#xff0c;例如&#xff0c;可以使用额外的运动数据来预测和分析日常发展&#xff0c;如人的体重和睡眠模式&a…

数据飞轮:打造业务增长的持续循环

在当今数据驱动的世界中&#xff0c;企业必须利用数据的力量才能保持竞争力。然而&#xff0c;仅仅收集和分析数据是不够的&#xff1b;企业必须能够从他们的数据中创造一个持续增长的循环&#xff0c;才能保持成功。其中一种方法就是创建数据飞轮。接下来让我们来探讨一下什么…

centos7如何连接网络 centos7wifi连接

这段时间重新学习 Linux 知识&#xff0c;用的是笔记本&#xff0c;连接的是无良房东家的 WiFi&#xff0c;IP地址经常变动。每次都要修改 Xshell 的配置才能连上虚拟机。效率很低。 为此&#xff0c;必须要解决这个 IP 地址经常变动的事情&#xff01;这里讲解的版本是&#…

Java集成gdal 处理解析tiff和shp数据

1. 配置 gdal 1.1. 官网下载 这个是因为你电脑是 win64 位才选择哦&#xff5e; 下载这个&#xff0c;然后解压 1.2. 复制这个压缩包下的 ddl 文件 可以按照类型复制&#xff0c;然后复制到你的 java jDK 文件夹下 1.3. 找到你的 java jdk 文件夹 不知道 java 的文件夹位置…

数字签名和CA数字证书的核心原理

看了蛋老师的视频就很容易理解了&#xff0c;首先对服务器的公钥和信息进行哈希运算得到一个短字符串&#xff0c;然后用CA机构中的私钥对这一短字符串进行加密就得到了一个数字签名&#xff0c;然后就这个数字签名放到数字证书中&#xff0c;同时服务器的公钥也放在数字证书中…

NFT Insider #148:The Sandbox 推出 SHIBUYA Y3K 时尚系列,Azuki 进军动漫 NFT 领域

市场数据 加密艺术及收藏品新闻 Infinex 新推 NFT 系列首四日销售额破4000万美元 尽管顶级 NFT 系列表现不佳&#xff0c;Infinex 的最新 NFT 系列在首四日内销售额已超过 4000 万美元。Infinex 是一个非托管平台&#xff0c;提供轻松访问链上协议和 dApp。 Infinex Core 的…

Day69补 前后端分离思想

ajax前后端分离 前后端分离处理&#xff1a;前端------&#xff08;数据&#xff09;-----服务端----&#xff08;数据&#xff09;-----前端-----动态改变页面的内容 1.json 1、JSON&#xff1a;由于JSON易读以及纯文本格式的特性&#xff0c;可以非常容易地与其他程序进行沟通…

引领长期投资新篇章:价值增长与财务安全的双重保障

随着全球金融市场的不断演变&#xff0c;长期投资策略因其稳健性和对价值增长的显著推动作用而日益受到投资者的重视。在这一背景下&#xff0c;Zeal Digital Shares&#xff08;ZDS&#xff09;项目以其创新的数字股票产品&#xff0c;为全球投资者提供了一个全新的长期投资平…

IPv6(四)

文章目录 Path MTUIPv6配置 Path MTU IPv4 对于数据过大的数据包会执行切片操作&#xff0c;但是切片有可能会造成设备性能的降低 IPv6使用Path MTU来传递数据过大的数据包 依次会协商最小的 MTU 单元为了减少中间转发设备的压力&#xff0c;中间转发设备不对 IPv6 报文进行分片…

【快速笔记】freeRTOS

第十八章 低功耗Tickless模式 睡眠模式:__WFI 中断唤醒 __WFE 事件唤醒 CPU CLK关闭 停止模式&#xff1a;RAM保持 中断唤醒 当 STM32F103 处于休眠模式的时候 Cortex-M3 内核停止运行&#xff0c;但是其他外设运行正常&#xff0c; 比如 NVIC、SRAM 等。 休眠模式的功耗比其他…