精通C++ STL(三):vector的介绍及使用

news2024/11/17 20:47:40

目录

vector的介绍

vector的使用

  vector的定义方式

  vector的空间增长问题

        size和capacity

        reserve和resize

        empty

  vector的迭代器使用

        begin和end

        rbegin和rend

  vector的增删查改

        push_back和pop_back

        insert和erase

        swap

  元素访问

        vector迭代器失效问题

        迭代器失效问题举例

        迭代器失效解决方法


vector的介绍

  1. 动态数组容器vector 是一种序列容器,它能够存储可变数量的元素,类似于数组,但更加灵活。

  2. 连续存储vector 采用连续内存空间来存储其元素,这使得通过索引访问元素变得可能,就像操作普通数组一样方便。

  3. 动态大小:与静态数组不同,vector 的容量可以根据需要自动调整,无需手动重新分配内存。

  4. 内存重新分配:当vector需要扩展其容量时,它会创建一个更大的新内存块,将现有元素复制到新位置,然后释放旧内存空间。

  5. 空间分配策略vector 通常会预留一些额外的存储空间,以便容纳未来可能增加的元素。这种策略有助于减少重新分配内存的频率,从而提高性能。不同的实现可能会采用不同的预留策略,以平衡空间使用和性能。

  6. 性能特点:由于vector的元素存储在连续的内存中,它在随机访问和在容器末尾添加或删除元素时表现出较高的效率。然而,如果在非末尾位置进行插入或删除操作,由于可能需要移动多个元素,其效率可能会降低。

vector的使用

        vector是C++标准模板库(STL)中的一个动态数组类,它在内存中以连续的方式存储元素,提供类似数组的功能但又具有动态大小和安全性。vector的主要优势在于它的灵活性和效率,能够自动处理内存分配和释放,使得程序员可以更专注于算法设计而不是底层内存管理。

  vector的定义方式

方式一: 构造一个某类型的空容器。

vector<int> v1; //构造int类型的空容器

方式二: 构造一个含有n个val的某类型容器。

vector<int> v2(10, 2); //构造含有10个2的int类型容器

方式三: 拷贝构造某类型容器的复制品。

vector<int> v3(v2); //拷贝构造int类型的v2容器的复制品

方式四: 使用迭代器拷贝构造某一段内容。

vector<int> v4(v2.begin(), v2.end()); //使用迭代器拷贝构造v2容器的某一段内容

注意:该方式也可用于拷贝其他容器的某一段内容。

string s("hello world");
vector<char> v5(s.begin(), s.end()); //拷贝构造string对象的某一段内容

  vector的空间增长问题

        size和capacity

  • 查询元素数量:使用size()函数可以快速得知容器中实际存储的元素数量。
  • 了解容器容量:通过调用capacity()函数,我们可以了解容器当前能够容纳的最大元素数,即容器的总容量。
#include <iostream>
#include <vector>
using namespace std;

int main()
{
	vector<int> v(10, 2);
	cout << v.size() << endl; //获取当前容器中的有效元素个数
	cout << v.capacity() << endl; //获取当前容器的最大容量
	return 0;
}

        reserve和resize

  • 调整容器容量reserve函数用于设定容器的最大容量。

    • 如果指定的值超过当前容量,容器会扩展以适应新的容量需求;

    • 如果指定的值小于或等于当前容量,则容器保持不变。

  • 改变元素数量resize函数允许我们改变容器中实际存储的元素数量。

    • 如果新指定的数量大于当前元素个数,容器会添加新元素,其值可以指定,若未指定则使用默认值;

    • 如果新指定的数量小于当前元素个数,容器将缩减至该数量。

#include <iostream>
#include <vector>
using namespace std;

int main()
{
	vector<int> v(10, 2);
	cout << v.size() << endl; //10
	cout << v.capacity() << endl; //10
	v.reserve(20); //改变容器的capacity为20,size不变
	cout << v.size() << endl; //10
	cout << v.capacity() << endl; //20
	v.resize(15); //改变容器的size为15
	cout << v.size() << endl; //15
	cout << v.capacity() << endl; //20
	return 0;
}

        empty

        调用empty()函数可以迅速判断容器内是否含有元素。若容器为空,函数返回一个布尔值true;若容器内有元素,则返回false

#include <iostream>
#include <vector>
using namespace std;

int main()
{
	vector<int> v(10, 2);
	cout << v.empty() << endl;
	return 0;
}

  vector的迭代器使用

 

        begin和end

  • 获取容器起始点begin()函数返回一个迭代器,指向容器中的第一个元素,允许用户从容器的起点开始遍历。

  • 定位容器末尾位置end()函数提供了一个迭代器,它位于容器最后一个元素之后的位置,标志着容器的结束,但不指向任何实际元素。

#include <iostream>
#include <vector>
using namespace std;

int main()
{
	vector<int> v(10, 2);
	//正向迭代器遍历容器
	vector<int>::iterator it = v.begin();
	while (it != v.end())
	{
		cout << *it << " ";
		it++;
	}
	cout << endl;
	return 0;
}

        rbegin和rend

  • 反向遍历的起点rbegin()函数提供了一个反向迭代器,它指向容器中的最后一个元素,允许从容器的末尾开始反向遍历。

  • 反向遍历的终点rend()函数返回一个反向迭代器,它位于容器第一个元素之前的位置,标志着反向遍历的结束。

#include <iostream>
#include <vector>
using namespace std;

int main()
{
	vector<int> v(10, 2);
	//反向迭代器遍历容器
	vector<int>::reverse_iterator rit = v.rbegin();
	while (rit != v.rend())
	{
		cout << *rit << " ";
		rit++;
	}
	cout << endl;
	return 0;
}

  vector的增删查改

        push_back和pop_back

  • 尾部插入操作push_back()函数用于在容器的末尾添加一个新元素,实现动态增长。

  • 尾部删除操作pop_back()函数用于移除容器末尾的元素,实现动态缩减。

#include <iostream>
#include <vector>
using namespace std;

int main()
{
	vector<int> v;
	v.push_back(1); //尾插元素1
	v.push_back(2); //尾插元素2
	v.push_back(3); //尾插元素3
	v.push_back(4); //尾插元素4

	v.pop_back(); //尾删元素
	v.pop_back(); //尾删元素
	v.pop_back(); //尾删元素
	v.pop_back(); //尾删元素
	return 0;
}

        insert和erase

  • 元素插入insert()函数允许我们在指定的迭代器位置插入单个或多个元素,从而在容器的特定位置扩展内容。

  • 元素删除erase()函数提供了删除指定迭代器位置的单个元素或删除迭代器定义的区间内所有元素的功能,遵循左闭右开的原则。

#include <iostream>
#include <vector>
using namespace std;

int main()
{
	vector<int> v;
	v.push_back(1);
	v.push_back(2);
	v.push_back(3);
	v.push_back(4);
	v.insert(v.begin(), 0); //在容器开头插入0
	
	v.insert(v.begin(), 5, -1); //在容器开头插入5个-1

	v.erase(v.begin()); //删除容器中的第一个元素

	v.erase(v.begin(), v.begin() + 5); //删除在该迭代器区间内的元素(左闭右开)
	
	return 0;
}

        swap

  • swap()函数提供了一种简便的方法来交换两个容器的数据内容,使得两个容器的数据空间可以快速互换。
#include <iostream>
#include <vector>
using namespace std;

int main()
{
	vector<int> v1(10, 1);
	vector<int> v2(10, 2);

	v1.swap(v2); //交换v1,v2的数据空间

	return 0;
}

  元素访问

        在vector中,通过重载的[ ]操作符,我们可以使用下标方式直接访问容器中的元素,这提供了一种直观且方便的方法。

#include <iostream>
#include <vector>
using namespace std;

int main()
{
	vector<int> v(10, 1);
	//使用“下标+[]”的方式遍历容器
	for (size_t i = 0; i < v.size(); i++)
	{
		cout << v[i] << " ";
	}
	cout << endl;
	return 0;
}

        由于vector支持迭代器,我们可以采用范围for循环来遍历容器中的所有元素。编译器在编译过程中会自动将范围for循环转换为迭代器的使用形式。 

#include <iostream>
#include <vector>
using namespace std;

int main()
{
	vector<int> v(10, 1);
	//范围for
	for (auto e : v)
	{
		cout << e << " ";
	}
	cout << endl;
	return 0;
}

        vector迭代器失效问题

  • 迭代器的抽象作用:迭代器的核心功能是为容器提供一种抽象机制,使用户在操作容器时无需了解其底层数据结构的复杂性。对于vector,迭代器本质上是指向容器存储空间的指针。

  • 迭代器失效的情况:当迭代器所关联的内存空间被释放或重新分配时,迭代器就会失效。这意味着迭代器指向的内存区域不再有效,如果继续使用这样的迭代器,可能会导致程序运行出错甚至崩溃

        迭代器失效问题举例

示例一:

#include <iostream>
#include <algorithm>
#include <vector>

using namespace std;

int main()
{
	vector<int> v;
	v.push_back(1);
	v.push_back(2);
	v.push_back(3);
	v.push_back(4);
	v.push_back(5);
	//v: 1 2 3 4 5
	vector<int>::iterator pos = find(v.begin(), v.end(), 2); //获取值为2的元素的迭代器
	v.insert(pos, 10); //在值为2的元素的位置插入10
	//v: 1 10 2 3 4 5
	v.erase(pos); //删除元素2 ???error(迭代器失效)
	//v: 1 2 3 4 5
	return 0;
}

        在我们的代码示例中,原本的计划是利用指向序列中特定位置(索引2)的迭代器,首先在该位置插入一个值10,随后删除原位置的元素2。

        但实际操作中,我们忽略了迭代器在插入操作后会指向新插入的元素10。因此,当我们执行删除操作时,实际上删除的是新插入的元素10,而非原计划要删除的元素2。

示例二:

#include <iostream>
#include <vector>

using namespace std;

int main()
{
	vector<int> v;
	for (size_t i = 1; i <= 6; i++)
	{
		v.push_back(i);
	}
	vector<int>::iterator it = v.begin();
	while (it != v.end())
	{
		if (*it % 2 == 0) //删除容器当中的全部偶数
		{
			v.erase(it);
		}
		it++;
	}
	return 0;
}

        该代码看上去实际上并没有什么错误,但如果你画图仔细分析,你就会发现该代码的问题所在,迭代器访问到了不属于容器的内存空间,导致程序崩溃。 

        不仅如此,而且在迭代器遍历容器中的元素进行判断时,并没有对1、3、5元素进行判断。 

        迭代器失效解决方法

        在进行容器操作时,应养成在每次使用迭代器之前进行重新赋值的习惯。这有助于维持迭代器的准确性和避免因迭代器失效而导致的问题。

示例一解决方法:

#include <iostream>
#include <algorithm>
#include <vector>

using namespace std;

int main()
{
	vector<int> v;
	v.push_back(1);
	v.push_back(2);
	v.push_back(3);
	v.push_back(4);
	v.push_back(5);
	//v: 1 2 3 4 5
	vector<int>::iterator pos = find(v.begin(), v.end(), 2); //获取值为2的元素的迭代器
	v.insert(pos, 10); //在值为2的元素的位置插入10
	//v: 1 10 2 3 4 5
	pos = find(v.begin(), v.end(), 2); //重新获取值为2的元素的迭代器
	v.erase(pos); //删除元素2
	//v: 1 10 3 4 5
	return 0;
}

        我们在使用迭代器删除元素2时对其进行重新赋值便可以解决。 

示例二解决方法:

#include <iostream>
#include <vector>

using namespace std;

int main()
{
	vector<int> v;
	for (size_t i = 1; i <= 6; i++)
	{
		v.push_back(i);
	}
	vector<int>::iterator it = v.begin();
	while (it != v.end())
	{
		if (*it % 2 == 0) //删除容器当中的全部偶数
		{
			it = v.erase(it); //删除后获取下一个元素的迭代器
		}
		else
		{
			it++; //是奇数则it++
		}
	}
	return 0;
}

        我们可以接收erase函数的返回值(erase函数返回删除元素的后一个元素的新位置),并且控制代码的逻辑:当元素被删除后继续判断该位置的元素(因为该位置的元素已经更新,需要再次判断)。

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

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

相关文章

vue3+Vite实现滑动拼图验证

参考文档&#xff1a;https://github.com/javaLuo/vue-puzzle-vcode/blob/master/README3.md 最近想学习一下这个前端滑动拼图的实现&#xff0c;就找了一个第三方库&#xff0c;该库支持vue2和vue3两个版本&#xff0c;直接看文档就能上手&#xff0c;我自己跑了一边倒&#…

武汉流星汇聚:青少年为何钟情亚马逊?一站式购物与信任铸就魅力

在当今这个数字化时代&#xff0c;青少年的消费习惯正以前所未有的速度演变&#xff0c;他们不仅是未来的消费主力军&#xff0c;更是推动市场变革的重要力量。令人瞩目的是&#xff0c;Piper Sandler最新发布的青少年消费研究报告揭示了一个引人注目的现象&#xff1a;超过半数…

快速下载大模型的方法

现在&#xff0c;每天都有各种大模型不断涌现&#xff0c;这些模型文件通常都很大。如何快速又靠谱地下载这些开源大模型&#xff0c;放到我们的环境中&#xff0c;进行后续的微调、量化和部署工作呢&#xff1f;以下是我的一些经验分享。 准备 Docker 基础环境 首先&#xf…

个人知识库与RAG的技术

构建个人知识库时&#xff0c;采用RAG结合LangChain的方法极为有效。RAG&#xff0c;即检索增强生成技术&#xff0c;是一种前沿的自然语言处理手段&#xff0c;它融合了信息检索的精确匹配与语言模型的高效文本生成&#xff0c;为处理自然语言相关任务提供了一种既灵活又准确的…

java~泛型

目录 泛型 泛型的声明 泛型的实例化 泛型的使用细节 自定义泛型类 自定义泛型接口 自定义泛型方法 泛型的继承和通配符 Junit 单元测试类 泛型 检查添加元素的类型 减少了类型转换的次数&#xff0c;直接对这个类型进行遍历&#xff0c;例如arraylist<>() publ…

Python酷库之旅-第三方库Pandas(072)

目录 一、用法精讲 291、pandas.Series.dt.round函数 291-1、语法 291-2、参数 291-3、功能 291-4、返回值 291-5、说明 291-6、用法 291-6-1、数据准备 291-6-2、代码示例 291-6-3、结果输出 292、pandas.Series.dt.floor函数 292-1、语法 292-2、参数 292-3、…

贪吃蛇游戏的实现:C++ 控制台版

功能概述 控制蛇的移动&#xff1a;使用WASD键控制蛇的移动方向。随机生成食物&#xff1a;蛇吃到食物后&#xff0c;食物会在游戏区域内随机生成。显示分数&#xff1a;游戏中会显示当前分数。游戏结束条件&#xff1a;当蛇碰到自己或走出边界时&#xff0c;游戏结束并显示“…

从巴黎到乐清,奥运精神引领全民健身新风尚!

16位火炬手接力&#xff0c;乐清点燃全民健身新篇章&#xff01; 作者&#xff1a;华夏之音总监&#xff0f;李望 在巴黎奥运会如火如荼进行的第11天&#xff0c;中国体育代表团以22枚金牌的骄人战绩领跑金牌榜&#xff0c;每一枚金牌都闪耀着中华体育精神的璀璨光芒&#xff…

c++ 连接mysql

其实就是MYsql c语言的API #define _CRT_SECURE_NO_WARNINGS 1 #define HOST "192.168.226.1" #define USER "root" #define PASSWORD "123456" #define PORT 3066#include <iostream> #include <stdlib.h> #include <mysql.…

【北斗授时服务】NTP网络时间服务器 安徽京准智造

【北斗授时服务】NTP网络时间服务器 安徽京准智造 【北斗授时服务】NTP网络时间服务器 安徽京准智造 一、NTP网络时间服务器产品介绍&#xff1a; NTP网络时间服务器是针对计算机、自动化装置等进行校时而研发的高科技设备&#xff0c;该产品可从GPS卫星&#xff08;北斗卫星、…

数据采集工具之Canal

本文主要介绍canal采集mysql数据的tcp、datahub(kafka)模式如何实现 1、下载canal https://aliyun-datahub.oss-cn-hangzhou.aliyuncs.com/tools/canal.deployer-1.1.5-SNAPSHOT.tar.gz 2、TCP模式的实现 a、canal.properties 打开看看即可&#xff0c;不需要调整 ######…

蚁群求解旅行商问题(TSP)的MATLAB例程

程序概况 输入需要经过的节点坐标&#xff1a; 运行程序后&#xff0c;即可得到&#xff1a; 运行结果 左图为遍历各点的运动轨迹&#xff0c;最终会回到起点右图为平均距离&#xff08;红线&#xff09;和最短距离在迭代时的变化情况 源代码 代码下载链接如下&#xff1a…

【工具类】JAVA (Android Studio )+ JS 加密解密 AES + Base 64

JAVA &#xff08;Android Studio &#xff09; JS 加密解密 AES Base 64 前言JAVA 代码&#xff08;解密&#xff09;JS代码&#xff08;加密&#xff09; 前言 整个过程&#xff1a; JS 接口先用AES加密&#xff0c;然后加密内容转Base64 编码&#xff1b;JAVA进行Base64解…

虹科干货 | 如何确保干冰运输的安全和稳定?

在上篇文章中&#xff0c;我们介绍了液氮罐运输和存储温度监测解决方案&#xff0c;本文我们将会了解医药供应链中干冰运输和温度监测的关键要点。 干冰在医药行业的应用 干冰是固体二氧化碳&#xff0c;当表面温度为 -78.5℃时&#xff0c;一块冷冻的干冰会直接转变为气体&am…

Ubuntu-18.04.1安装JetBrains PyCharm 2018.1.6 专业版(永久破解方法)

软件安装包下载地址&#xff1a;Other Versions - PyCharm 将安装包放置Ubuntu系统中解压&#xff0c;到bin目录下找到pycharm.sh即可打开。 补丁破解方式&#xff08;需关闭软件pycharm&#xff0c;否则会打不开pycharm&#xff01;&#xff01;&#xff01;&#xff09;&am…

昂科烧录器支持MindMotion灵动微电子的32位微控制器MM32F5287L9P

芯片烧录行业领导者-昂科技术近日发布最新的烧录软件更新及新增支持的芯片型号列表&#xff0c;其中MindMotion灵动微电子的32位微控制器MM32F5287L9P已经被昂科的通用烧录平台AP8000所支持。 MM32F5287L9P搭载Armv8-M 架构“星辰”STAR-MC1处理器&#xff0c;最高工作频率可达…

CSS技巧专栏:一日一例 20-纯CSS实现点击会凹陷的按钮

本例图片 案例分析 其实这个按钮非常的简单啊&#xff0c;主要就是利用了box-shadow的inset。 布局代码 <button class"base">凹下的按钮</button> 基础样式 :root{--main-bg-color: #dcdcdc; /* 将页面背景色调整为浅灰色 */--color:#000;--hover-…

Cesium手动建模模型用Cesiumlab转3D Tiles模型位置不对,调整模型位置至指定经纬度

Cesium加载3Dtiles模型的平移和旋转_3dtiles先旋转再平移示例-CSDN博客 Cesium 平移cesiumlab生产的3Dtiles切片模型到目标经纬度-CSDN博客 【ArcGISCityEngine】自行制作Lod1城市大尺度白膜数据_cityengine 生成指定坐标集指定区域的白模-CSDN博客 以上次ArcGISCityEngine制…

IEEE Transactions on Intelligent Transportation Systems投稿指南

投稿记录 submitted 2024-5-29 Awaiting AE Assignment 2024-6-11 Under review 2024-6-15 Awaiting EIC Decision 2024-6-24 感觉要拒稿的节奏 Resubmit To Another Journal 2024-6-25 与期刊不符合 下载模板 IEEE Transactions on Intelligent Transportation Syste…

java.lang.NoClassDefFoundError: ch/qos/logback/core/util/StatusPrinter2

1、问题 SpringBoot升级报错&#xff1a; Exception in thread "main" java.lang.NoClassDefFoundError: ch/qos/logback/core/util/StatusPrinter2 类找不到&#xff1a; Caused by: java.lang.ClassNotFoundException: ch.qos.logback.core.util.StatusPrinter22、…