[STL]vector使用介绍

news2024/11/28 0:48:22

[STL]vector使用介绍

注:文内代码均在Visual Studio 2013下进行测试,不同的编译器下在扩容大小等方面可能有所不同,但不影响各接口函数的使用。

文章目录

  • [STL]vector使用介绍
    • 1. vector介绍
    • 2. 构造函数
    • 3. 迭代器相关函数
      • begin函数和end函数的使用
      • rbegin和rend函数的使用
    • 4. 容量相关函数
      • size函数和capacity函数
      • empty函数
      • reserve函数和resize函数 (扩容函数)
      • shrink_to_fit函数
    • 5. 数据修改函数
      • push_back函数
      • insert函数和erase函数
      • 迭代器失效问题
      • clear函数
    • 6. 数据访问函数
      • []运算符重载

1. vector介绍

  1. vector是表示可变大小数组的序列容器。
  2. vector就像数组一样,对其元素使用连续的存储位置,这意味着也可以用下标的方式访问数据,并且与在数组中一样高效。
  3. vector与数组不同的是,它们的大小可以动态变化,它们的存储由容器自动处理。
  4. vector使用动态分配的数组来存储其元素,在插入新元素时原有空间不足,会重新分配空间,并将原有数据拷贝至新空间。
  5. vector分配空间策略:vector会分配一些额外的空间以适应可能的增长,因此存储空间比实际需要的存储空间一般更大。不同的库采用不同的策略权衡空间的使用和重新分配,以至于在末尾插入一个元素的时候是在常数的时间复杂度完成的。
  6. 由于vector采用连续的空间来存储元素,与其他动态序列容器相比,vector在访问元素的时候更加高效,在其末尾添加和删除元素相对高效,而对于不在其末尾进行的删除和插入操作效率则相对较低。

2. 构造函数

vector构造函数声明:

image-20230611211535114

(1)构造函数定义示例:

vector<int> v; //构造一个int类型的空容器

由于这是一个全缺省的函数,因此不需要传入参数(默认构造函数),并且生成的容器是没有申请空间的。

(2)构造函数定义示例:

vector<int> v(10, 1); //构造一个可以存储10个int类型的容器,并且将数据都初始化成1

(3)构造函数定义示例:

vector<int> v1(10, 1);
vector<int> v2(v1.begin(), v1.end()); //构造一个存储int类型空容器v2,然后将v1的数据依次插入

由于是一个模板类型,因此只要迭代器指向的数据可以存入到该vector中都可以使用这个构造函数。比如:

string s("hello world");
vector<int> v(v1.begin(), v1.end()); //由于char类型可以存入int类型数据的空间,因此也是合法

(4)构造函数定义示例:

vector<int> v1(10, 1);
vector<int> v2(v1); //通过v1拷贝构造v2

此函数是拷贝构造函数。

3. 迭代器相关函数

begin函数和end函数的使用

image-20230612172146204

begin函数会返回一个正向迭代器,该迭代器指向vector存放的第一个数据的位置。

image-20230612172200284

end函数会返回一个正向迭代器,该迭代器指向vector存放的最后一个数据后的第一个数据的位置。

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

int main()
{
	vector<int> v(10, 2);
	//正向迭代器遍历容器
	vector<int>::iterator it = v.begin(); //定义存放int类型数据的vector的正向迭代器it接受begin函数的返回值
	while (it != v.end())
	{
		cout << *it << " "; //使用*操作符获取迭代器指向的数据
		it++; //使迭代器指向下一个数据的位置
	}
	cout << endl;
	return 0;
}

另外begin函数和end函数也重载了返回只读正向迭代器的版本,和正向迭代器的区别就是不能通过该迭代器修改指向的数据。

image-20230612172435807

vector<int>::const_iterator cit = v.begin(); //定义存放int类型数据的vector的只读正向迭代器cit接受begin函数的返回值

rbegin和rend函数的使用

image-20230612172218611

rbegin函数会返回一个反向迭代器,该迭代器指向vector存放的最后一个数据的位置。

image-20230612172235294

rend函数会返回一个正向迭代器,该迭代器指向vector存放的第一个数据前的第一个数据的位置。

#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;
}

同样的rbegin函数和rend函数也重载了返回只读反向迭代器的版本。

vector<int>::const_reverse_iterator crit = v.rbegin(); 

4. 容量相关函数

size函数和capacity函数

size函数会返回容器所存储的有效数据个数,capacity函数会返回容器的容量。

**注意:**容量和有效数据个数不一定是相等的,比如容器的容量是存储十个数据,但是容器内可能只存储了5个有效数据。但是容器存储的有效数据个数不可能大于容量,那样就是越界存储了。

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

int main()
{
	vector<int> v(10, 2);
	cout << v.size() << endl;      //输出容器存储的有效数据个数
	cout << v.capacity() << endl;  //输出容器的容量
	return 0;
}

empty函数

返回一个bool值,如果容器为空返回真,容器不为空返回假。

vector<int> v;
cout << v.empty() << endl; //返回值为真

reserve函数和resize函数 (扩容函数)

reserve函数和resize函数都能给容器扩容,reserve函数只是改变容器容量大小,resize函数不仅改变容量大小,还将容器的有效数据个数和容量保持一致。

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

int main()
{
	vector<int> v;
	v.reserve(10);
	cout << v.size() << endl;     //输出为0
	cout << v.capacity() << endl; //输出为10
	return 0;
}
#include <iostream>
#include <vector>
using namespace std;

int main()
{
	vector<int> v;
	v.resize(10);
	cout << v.size() << endl;  	  //输出为10
	cout << v.capacity() << endl; //输出为10
	return 0;
}

另外如果reserve函数和resize函数传入的参数小于容器容量,两个函数的处理方式也是不同的,reserve函数什么都不做,resize函数会将有效数据个数改为参数大小,容器容量不变。

关于resize函数还需要补充一点:

image-20230612173732338

第一个参数大于有效数据个数时,会将原有有效数据后的所有数据改为第二个参数的值。如果第二个参数未指定,则改用默认构造函数生成的匿名对象。

shrink_to_fit函数

shrink_to_fit函数会将容器容量缩小为有效数据个数的大小。

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

int main()
{
	vector<int> v;
	v.resize(10);
	cout << v.capacity() << endl;
	v.shrink_to_fit();
	cout << v.capacity() << endl;
	return 0;
}

**注意:**C/C++不支持将申请的空间里的一部分单独释放,因此缩容的实现是申请一块新的空间,然后将数据拷贝,代价很大,不建议使用。

5. 数据修改函数

push_back函数

push_back函数是将数据尾插到容器内,也就是插入到end函数返回的迭代器指向的位置,如果容器容量不够,容器会按照一定规则进行扩容。

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

int main()
{
	vector<int> v(4, 1);
	cout << v.size() << endl;      	//输出为4
	cout << v.capacity() << endl;	//输出为4

	v.push_back(5);
	cout << v.size() << endl;		//输出为5
	cout << v.capacity() << endl;	//输出为6
	return 0;
}

insert函数和erase函数

由于insert函数和erase函数使用与迭代器有关,因此需要一个获取指向指定数据的迭代器的函数:find函数

image-20230613144625093

vector中没有实现find函数,find函数是实现在algorithm库中的,是一个模板实现的通用函数,不止vector,所有STL中的容器都能使用。

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

int main()
{
	vector<int> v;
	v.push_back(1);
	v.push_back(2);
	v.push_back(3);
	vector<int>::iterator it = find(v.begin(), v.end(), 2); //获取指向数据2的迭代器
	return 0;
}

insert函数可以在迭代器指向的位置插入数据。

image-20230613145716025

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

int main()
{
	vector<int> v;
	v.push_back(1);
	v.push_back(2);
	v.push_back(3);
	vector<int>::iterator it = find(v.begin(), v.end(), 2);
	v.insert(it, 66);	
	return 0;
}

erase函数能够将迭代器指向的数据删除。

image-20230613145942595

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

int main()
{
	vector<int> v;
	v.push_back(1);
	v.push_back(2);
	v.push_back(3);
	vector<int>::iterator it = find(v.begin(), v.end(), 2);
	v.insert(it, 66);
    
	it = find(v.begin(), v.end(), 66);
	v.erase(it);
	return 0;
}

迭代器失效问题

insert函数:

情况1:由于容器扩容导致的迭代器指向的数据位置不正确的失效问题。

插入前为了找到正确的插入位置,使用迭代器pos指向插入的位置,由于容器满了,容器扩容到新的空间,而pos指向原有的位置导致pos迭代器失效。

image-20230615182402188

#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
int main()
{
	vector<int> v;
	v.push_back(1);
	v.push_back(2);
	v.push_back(3);
	v.push_back(4);
	vector<int>::iterator pos = find(v.begin(), v.end(), 1);
	v.insert(pos, 66);
	for (auto e : v)
	{
		cout << e << " ";
	}
	cout << endl;
	v.insert(pos, 6); //扩容使得迭代器pos失效,程序报错
	return 0;
}

解决办法:

由于insert函数的返回值是一个迭代器,该迭代器指向插入数据的位置,也就是说如果扩容了会指向新的空间里的插入位置,用pos接受insert函数的返回值即可。

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

int main()
{
	vector<int> v;
	v.push_back(1);
	v.push_back(2);
	v.push_back(3);
	v.push_back(4);
	vector<int>::iterator pos = find(v.begin(), v.end(), 1);
	pos = v.insert(pos, 66);//pos接受返回值避免迭代器失效
	for (auto e : v)
	{
		cout << e << " ";
	}
	cout << endl;
	v.insert(pos, 6);
	for (auto e : v)
	{
		cout << e << " ";
	}
	cout << endl;
	return 0;
}

情况2:由于插入后指向不变导致的迭代器指向的数据不正确的失效问题。

插入前为了找到正确的插入位置,使用迭代器pos指向插入的位置,由于插入后pos指向的空间不变,因此pos不再指向开始指向的数据。

解决办法:

重新对pos进行赋值(insert函数的返回的迭代器指向的是插入的数据的位置)。

#include <iostream>
#include <vector>
#include <algorithm>
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);
	vector<int>::iterator pos = find(v.begin(), v.end(), 1);
	v.insert(pos, 66);
	for (auto e : v)
	{
		cout << e << " ";
	}
	cout << endl;
	pos = find(v.begin(), v.end(), 1);
	(*pos)++;
	for (auto e : v)
	{
		cout << e << " ";
	}
	cout << endl;
	return 0;
}

**总结:**由于容器的内部实现我们不知道,因此什么情况下扩容,这是不取决于编译器的,也就是不可控的,因此使用完insert我们最好默认pos是失效的,避免代码出现问题。

erase函数

erase函数造成的迭代器失效是因为迭代器指向的位置在删除后不存在有效数据了,导致迭代器指向有效数据外的位置导致迭代器失效。

image-20230615215637120

但是erase函数的问题不止于此,由于不同编译器的STL实现不同,erase函数的使用结果具有不确定性。

我们可以看下一段代码:

#include <iostream>//错误示例
#include <vector>
#include <algorithm>
using namespace std;

int main()
{
	std::vector<int> v;
	v.push_back(1);
	v.push_back(2);
	v.push_back(3);
	v.push_back(4);
	vector<int>::iterator pos = find(v.begin(), v.end(), 2);
	v.erase(pos);
	for (auto e : v)
	{
		cout << e << " ";
	}
	cout << endl;
	*(pos) += 10;
	for (auto e : v)
	{
		cout << e << " ";
	}
	cout << endl;
	return 0;
}

首先这段代码在Visual Studio系列的编译器下是绝对报错的,因为Visual Studio使用的STL版本下vector的迭代器使用类封装的,迭代器的使用有着严格的检测,因此在Visual Studio下erase函数使用的迭代器如果不重新赋值,是不能使用的,如果使用时会断言报错的。注意报错的原因是因为使用pos调用了erase函数,然后没重新赋值又对pos使用,不是pos的指向无效数据区才报错。

image-20230615220816841

将这段代码在Linux下进行测试(Linux使用的是g++编译器):

image-20230615220752667

Linux下是不会报错的,并且在利用pos调用了erase函数后,访问pos修改数据都没报错,这与Linux实现迭代器使用的是原生指针有关,Linux的实现下只有迭代器指向无效数据区的访问才报错。总之仅仅是g++和Visual Studio下就发生了不同结果。

解决办法:

由于是因为结果不确定造成的迭代器失效,因此需要在迭代器使用后重新赋值,由于erase函数会返回指向插入位置的迭代器,因此可以选择使用接受返回值来解决。

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

int main()
{
	std::vector<int> v;
	v.push_back(1);
	v.push_back(2);
	v.push_back(3);
	v.push_back(4);
	vector<int>::iterator pos = find(v.begin(), v.end(), 2);
	pos = v.erase(pos);
	for (auto e : v)
	{
		cout << e << " ";
	}
	cout << endl;
	*(pos) += 10;
	for (auto e : v)
	{
		cout << e << " ";
	}
	cout << endl;
	return 0;
}

**总结:**由于erase函数的运行结果具有不确定性,因此我们必须认为erase函数使用的迭代器在使用后就失效了,需要重新赋值使用。

clear函数

将容器内数据清空。(实际是将容器有效数据个数修改为0)

#include <iostream>
#include <vector>
using namespace std;
int main()
{
	vector<int> v(100, 6);
	v.clear();
	return 0;
}

6. 数据访问函数

[]运算符重载

image-20230613150843513

前面提到了,由于vector可以像数组一样使用,就是因为重载了[]运算符,不同于数组的是它会对越界访问进行检查。

#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);
	for (size_t i = 0; i < v.size(); ++i)
	{
		cout << v[i] << " ";	
	}
	cout << endl;	//输出结果为 1 2 3 4
	return 0;
}

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

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

相关文章

实现点击复制到剪切板功能

该功能使用VueUse实现 什么是 VueUse VueUse不是Vue.use&#xff0c;它是为Vue 2和3服务的一套Vue Composition API的常用工具集&#xff0c;是目前世界上Star最高的同类型库之一。它的初衷就是将一切原本并不支持响应式的JS API变得支持响应式&#xff0c;省去程序员自己写相…

jmeter常用的提取器(正则表达式和JSON提取器)

jmeter常用的后置处理器有两种提取数据&#xff1a; 1、JSON提取器 获取后可以将变量token引用到其他所需要的地方 &#xff08;正则表达式和JSON提取器&#xff09;:2023接口自动化测试框架必会两大神器:正则提取器和Jsonpath提取器_哔哩哔哩_bilibilihttps://www.bilibili.…

uniapp实战

上面是tab栏&#xff0c;下面是swiper&#xff0c;&#xff0c;tab和swiper和 红色滑块 动态变化&#xff0c;&#xff0c; 遇到的问题&#xff1a; 往下滚动 tab栏 吸顶&#xff1a; position:sticky; z-index:99; top:0;swiper切换触发 change 事件&#xff0c; :current …

SOMEIP协议--第四节[ SOME/IP](someip概述与行为)

SOMEIP协议–第四节[ SOME/IP](someip概述与行为) 文章目录 SOMEIP协议--第四节[ SOME/IP](someip概述与行为)1、概述2、someip的行为2.1 基础传输2.2 SOME/IP-TP传输:2.3 someip参数(client)2.4 someip参数(server)1、概述 Method | Event | Field是上层设计的三个概念…

【C++】优先级队列和反向迭代器 模拟笔记

文章目录 优先级队列仿函数适配器模式堆知识储备 反向迭代器代码&#xff08;反向迭代器&#xff09;代码&#xff08;优先级队列&#xff09; 优先级队列 仿函数 仿函数&#xff0c;它不是函数&#xff08;其实是个类&#xff09;&#xff0c;但用法和函数一样。既然是个类&a…

子类化QThread来实现多线程,moveToThread函数的作用

子类化QThread来实现多线程&#xff0c; QThread只有run函数是在新线程里的&#xff0c;其他所有函数都在QThread生成的线程里。正确启动线程的方法是调用QThread::start()来启动。 一、步骤 子类化 QThread&#xff1b;重写run&#xff0c;将耗时的事件放到此函数执行&#…

轻量级Web报表工具ActiveReportsJS全新发布v4.0,支持集成更多前端框架!

ActiveReportsJS 是一款基于 JavaScript 和 HTML5 的轻量级Web报表工具&#xff0c;采用拖拽式设计模式&#xff0c;不需任何服务器和组件支持&#xff0c;即可在 Mac、Linux 和 Windows 操作系统中&#xff0c;设计多种类型的报表。ActiveReportsJS 同时提供跨平台报表设计、纯…

18.背景轮播

背景轮播 html部分 <div class"container"><div class"slide active" style"background-image: url(./static/20180529205331_yhGyf.jpeg);"></div><div class"slide " style"background-image: url(./s…

vue3+taro+Nutui 开发小程序(二)

上一篇我们初始化了小程序项目&#xff0c;这一篇我们来整理一下框架 首先可以看到我的项目整理框架是这样的&#xff1a; components:这里存放封装的组件 custom-tab-bar:这里存放自己封装的自定义tabbar interface&#xff1a;这里放置了Ts的一些基本泛型&#xff0c;不用…

AtcoderABC238场

A - Exponential or QuadraticA - Exponential or Quadratic 题目大意 给定一个整数 n&#xff0c;判断是否满足 2n >n 2。 思路分析 根据数学知识可知n 的取值在 2 到 4 之间&#xff08;包括 2 和 4&#xff09;&#xff0c;不满足条件 。 时间复杂度 O(1) AC代码 …

MyBatis学习笔记——4

MyBatis学习笔记——4 一、MyBatis的高级映射及延迟加载1.1、多对一1.1.1、第一种方式&#xff1a;级联属性映射1.1.2、第二种方式&#xff1a;association1.1.3、第三种方式&#xff1a;分步查询 1.2、一对多1.2.1、第一种方式&#xff1a;collection1.2.1、第二种方式&#x…

Linux Ubuntu crontab 添加错误 提示:no crontab for root - using an empty one 888

资料 错误提示&#xff1a; no crontab for root - using an empty one 888 原因剖析&#xff1a; 第一次使用crontab -e 命令时会让我们选择编辑器&#xff0c;很多人会不小心选择默认的nano&#xff08;不好用&#xff09;&#xff0c;或则提示no crontab for root - usin…

一文了解Python中的while循环语句

目录 &#x1f969;循环语句是什么 &#x1f969;while循环 &#x1f969;遍历猜数字 &#x1f969;while循环嵌套 &#x1f969;while循环嵌套案例 &#x1f990;博客主页&#xff1a;大虾好吃吗的博客 &#x1f990;专栏地址&#xff1a;Python从入门到精通专栏 循环语句是什…

【N32L40X】学习笔记11-ADC规则通道采集+dma数据传输

ADC规则通道转换 概述 支持 1 个 ADC&#xff0c;支持单端输入和差分输入&#xff0c;最多可测量 16 个外部和 3 个内部源。支持 12 位、10 位、8 位、6 位分辨率。ADC 时钟源分为工作时钟源、采样时钟源和计时时钟源 仅可配置 AHB_CLK 作为工作时钟源。可配置 PLL 作为采样时…

【安全】Sqllabs(1-4) 多种情况浅析

目录 环境⭐ 先要 ⭐⭐⭐⭐⭐ Less - 1 (information_shcema) Less - 2 (假设没有information_schema) Less - 3 (无列名注入) Less - 4 环境⭐ MySQL8.0.12 Nginx1.15.11 先要 ⭐⭐⭐⭐⭐ MySQL5.0以上有这几个数据库mysql, sys&#xff0c;information_schema informa…

前端性能优化——图片优化

一、图片优化措施 优化图片是 Web 前端优化的重要一环&#xff0c;因为图片是 Web 页面中最耗费带宽和加载时间的资源之一。以下是一些通过优化图片来优化 Web 前端的方法&#xff1a; 压缩图片&#xff1a;压缩图片可以减少图片的文件大小&#xff0c;从而减少加载时间。 使…

【数学建模】相关是一个距离指标吗?

一、说明 本文探讨最平凡的数学模型--距离模型。我们知道&#xff0c;任何数学模型如果是个距离模型&#xff0c;那么它是&#xff1a;放心的、自动的、不加任意条件的指标项目。然而另一些度量参数不是距离空间&#xff0c;因此&#xff0c;使用起来必须外加若干条件&#xff…

苹果iOS 16.6 RC发布:或为iPhone X/8系列养老版本

今天苹果向iPhone用户推送了iOS 16.6 RC更新(内部版本号&#xff1a;20G75)&#xff0c;这是时隔两个月的首次更新。 按照惯例RC版基本不会有什么问题&#xff0c;会在最近一段时间内直接变成正式版&#xff0c;向所有用户推送。 需要注意的是&#xff0c;鉴于iOS 17正式版即将…

【CN-Docker】window11下Docker下开启kubernetes

1. 安装Docker 安装docker步骤如下&#xff1a; 下载Docker启用hyper-v 2.1.powershell&#xff0c;管理员运行Enable-WindowsOptionalFeature -Online -FeatureName Microsoft-Hyper-V -All安装wsl配置Docker镜像加速地址(阿里云) 4.1. "registry-mirrors": [&quo…

Qt/C++音视频开发48-推流到rtsp服务器

一、前言 之前已经打通了rtmp的推流&#xff0c;理论上按照同样的代码&#xff0c;只要将rtmp推流地址换成rtsp推流地址&#xff0c;然后格式将flv换成rtsp就行&#xff0c;无奈直接遇到协议不支持的错误提示&#xff0c;网上说要换成rtp&#xff0c;换了也没用&#xff0c;而…