【C++STL】“vector“用法 入门必备 超详细

news2024/9/25 19:23:56

vector用法

  • 什么是vector?
  • vector的使用
    • vector的定义(构造函数)
    • vector iterator 的使用
      • 迭代器演示
      • 范围for
    • vector 空间增长
      • 扩容机制
    • vector 的增删查改
    • assign
    • vector 迭代器失效问题。
  • 🍀小结🍀

🎉博客主页:小智_x0___0x_
🎉欢迎关注:👍点赞🙌收藏✍️留言
🎉系列专栏:C++初阶
🎉代码仓库:小智的代码仓库

什么是vector?

  1. vector是表示可变大小数组的序列容器。
  2. 就像数组一样,vector也采用的连续存储空间来存储元素。也就是意味着可以采用下标对vector的元素进行访问,和数组一样高效。但是又不像组,它的大小是可以动态改变的,而且它的大小会被容器自动处理。
  3. 本质讲,vector使用动态分配数组来存储它的元素。当新元素插入时候,这个数组需要被重新分配大小为了增加存储空间。其做法是,分配一个新的数组,然后将全部元素移到这个数组。就时间而言,这是一个相对代价高的任务,因为每当一个新的元素加入到容器的时候,vector并不会每次都重新分配大小。
  4. vector分配空间策略:vector会分配一些额外的空间以适应可能的增长,因为存储空间比实际需要的存储空间更大。不同的库采用不同的策略权衡空间的使用和重新分配。但是无论如何,重新分配都应该是对数增长的间隔大小,以至于在末尾插入一个元素的时候是在常数时间的复杂度完成的。
  5. 因此,vector占用了更多的存储空间,为了获得管理存储空间的能力,并且以一种有效的方式动态增长。
  6. 与其它动态序列容器相比(deque, list and forward_list), vector在访问元素的时候更加高效,在末尾添加和删除元素相对高效。对于其它不在末尾的删除和插入操作,效率更低。比起list和forward_list统一的迭代器和引用更好。

使用STL的三个境界:能用,明理,能扩展 ,那么下面学习vector,我们也是按照这个方法去学习

vector的使用

vector学习时要学会查看文档:vector的文档介绍,vector在实际中非常的重要,在实际中我们熟悉常见的接口就可以。

vector的定义(构造函数)

在这里插入图片描述

(constructor)构造函数声明接口说明
vector()(重点)无参构造
vector(size_type n, const value_type& val = value_type())构造并初始化n个val
vector (const vector& x); (重点)拷贝构造
vector (InputIterator first, InputIterator last);使用迭代器进行初始化构造

eg:


int main()
{
	//无参构造
	vector<int> v1;
	cout << "v1:";
	for (auto e : v1)
	{
		cout << e << " ";
	}
	cout << endl;
	//构造并初始化n个val
	vector<int> v2(10, 1);
	cout << "v2:";
	for (auto e : v2)
	{
		cout << e << " ";
	}
	cout << endl;
	//拷贝构造
	vector<int> v3(v2);
	cout << "v3:";
	for (auto e : v3)
	{
		cout << e << " ";
	}
	cout << endl;
	//使用迭代器进行初始化构造
	vector<int> v4(v2.begin(), v2.end());
	cout << "v4:";
	for (auto e : v4)
	{
		cout << e << " ";
	}
	cout << endl;

	return 0;
}

运行结果>
在这里插入图片描述

vector iterator 的使用

iterator的使用接口说明
begin +end(重点)获取第一个数据位置的iterator/const_iterator, 获取最后一个数据的下一个位置的iterator/const_iterator
rbegin + rend获取最后一个数据位置的reverse_iterator,获取第一个数据前一个位置的reverse_iterator

在这里插入图片描述

迭代器演示

int main()
{
	vector<int> v(10, 2);
	vector<int>::iterator it = v.begin();
	while (it != v.end())
	{
		cout << *it << " ";
		++it;
	}
	cout << endl;
	return 0;
}

运行结果>
在这里插入图片描述

范围for

实际中我们很少使用迭代器,反而会经常使用范围for对数据进行操作>

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

运行结果>
在这里插入图片描述

vector 空间增长

容量空间接口说明
size获取数据个数
capacity获取容量大小
empty判断是否为空
resize(重点)改变vector的size
reserve (重点)改变vector的capacity
int main()
{
	vector<int> v(10, 2);
	for (auto e : v)
	{
		cout << e << " ";
	}
	cout << endl;
	//size()
	cout << "v.size():" << v.size() << endl;
	//capacity()
	cout << "v.capacity():" << v.capacity() << endl;
	//empty()
	cout << "v.empty():" << v.empty() << endl;
	//resize()
	v.resize(5);
	cout << "改变后v.size():" << v.size() << endl;
	//reserve()
	v.reserve(100);
	cout << "改变后v.capacity():" << v.capacity() << endl;
	return 0;
}

运行结果>:
在这里插入图片描述

  • reserve只负责开辟空间,如果确定知道需要用多少空间,reserve可以缓解vector增容的代价缺陷问题。
  • resize在开空间的同时还会进行初始化(如果指定了val,则新元素将被初始化为val,否则,它们将被值初始化。),影响size。

扩容机制

//测试扩容机制代码
void TestVectorExpand()
{
	size_t sz;
	vector<int> v;
	sz = v.capacity();
	cout << "making v grow:\n";
	for (int i = 0; i < 100; ++i)
	{
		v.push_back(i);
		if (sz != v.capacity())
		{
			sz = v.capacity();
			cout << "capacity changed: " << sz << '\n';
		}
	}
}

vs2019下运行:
在这里插入图片描述
g++编译器下运行:

在这里插入图片描述
总结:
capacity的代码在vs和g++下分别运行会发现,vs下capacity是按1.5倍增长的,g++是按2倍增长的。
这个问题经常会考察,不要固化的认为,vector增容都是2倍,具体增长多少是根据具体的需求定义
的。vs是PJ版本STL,g++是SGI版本STL。

// 如果已经确定vector中要存储元素大概个数,可以提前将空间设置足够
// 就可以避免边插入边扩容导致效率低下的问题了
void TestVectorExpandOP()
{
	vector<int> v;
	size_t sz = v.capacity();
	v.reserve(100); // 提前将容量设置好,可以避免一遍插入一遍扩容
	cout << "making bar grow:\n";
	for (int i = 0; i < 100; ++i)
	{
		v.push_back(i);
		if (sz != v.capacity())
		{
			sz = v.capacity();
			cout << "capacity changed: " << sz << '\n';
		}
	}
}

在这里插入图片描述

vector 的增删查改

vector增删查改接口说明
push_back尾插
pop_back尾删
find查找(注意这个是算法模块实现,不是vector的成员接口)
insert在position之前插入val
erase删除position位置的数据
swap交换两个vector的数据空间
operator[ ]像数组一样访问

eg:

#include<algorithm>
int main()
{
	vector<int> v(5,1);
	cout << "初始化:" << endl;
	for (auto e : v)
	{
		cout << e << " ";
	}
	cout << endl;
	v.push_back(2);
	v.push_back(3);
	v.push_back(4);
	cout << "依次尾插2,3,4:" << endl;
	for (auto e : v)
	{
		cout << e << " ";
	}
	cout << endl;
	v.pop_back();
	cout << "尾删" << endl;
	for (auto e : v)
	{
		cout << e << " ";
	}
	cout << endl;

	auto pos=find(v.begin(),v.end(), 3);
	cout << "在3前面插入100:"<<endl;
	v.insert(pos, 100);
	for (auto e : v)
	{
		cout << e << " ";
	}
	cout << endl;
	
	pos = find(v.begin(), v.end(), 3);
	cout << "删除3:"<<endl;
	v.erase(pos);
	for (auto e : v)
	{
		cout << e << " ";
	}
	cout << endl;

	cout << "交换前:" << endl;
	vector<int> v1(10, 1);
	cout << "v:";
	for (auto e : v)
	{
		cout << e << " ";
	}
	cout << endl;
	cout << "v1:";
	for (auto e : v1)
	{
		cout << e << " ";
	}
	cout << endl;

	swap(v, v1);

	cout << "交换后:" << endl;
	cout << "v:";
	for (auto e : v)
	{
		cout << e << " ";
	}
	cout << endl;
	cout << "v1:";
	for (auto e : v1)
	{
		cout << e << " ";
	}
	cout << endl;
	return 0;
}

运行结果:
在这里插入图片描述

assign

assign是C++ STL中vector容器的一个成员函数,用于为vector分配新的元素并替换现有元素。它可以接受多种类型的参数,例如另一个vector、数组、迭代器等。
下面是一个简单的示例:

#include <iostream>
#include <vector>

int main() {
  std::vector<int> v1(5); // 创建一个包含5个元素的vector
  std::vector<int> v2(3, 7); // 创建一个包含3个值为7的元素的vector

  v1.assign(v2.begin(), v2.end()); // 使用v2中的元素替换v1中的元素

  std::cout << "v1 contains:";
  for (int i : v1) {
    std::cout << ' ' << i;
  }
  std::cout << '\n';

  return 0;
}

输出结果:

v1 contains: 7 7 7

在上面的示例中,assign函数将v2中的元素替换了v1中的元素。由于v2包含3个值为7的元素,因此v1中原来的5个元素都被替换成了7。

vector 迭代器失效问题。

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

  1. 会引起其底层空间改变的操作,都有可能是迭代器失效,比如:resize、reserve、insert、assign、push_back等。
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
int main()
{
	int a[] = { 1, 2, 3, 4 };
	vector<int> v(a, a + sizeof(a) / sizeof(int));
	// 使用find查找3所在位置的iterator
	auto 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;
}
// 2. erase删除任意位置代码后,linux下迭代器并没有失效
// 因为空间还是原来的空间,后序元素往前搬移了,it的位置还是有效的
int main()
{
    vector<int> v{1,2,3,4,5};
    auto it = find(v.begin(), v.end(), 3);
    v.erase(it);
    cout << *it << endl;
    while(it != v.end())
    {
        cout << *it << " ";
        ++it;
    }
    cout << endl;
    return 0;
}

在这里插入图片描述

// 3: erase删除的迭代器如果是最后一个元素,删除之后it已经超过end
// 此时迭代器是无效的,++it导致程序崩溃
int main()
{
	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;
}

运行结果:
在这里插入图片描述
可以看到此时的程序已经崩溃。

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

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

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

🍀小结🍀

今天我们认识了STL中”vector“的用法相信大家看完有一定的收获。
种一棵树的最好时间是十年前,其次是现在! 把握好当下,合理利用时间努力奋斗,相信大家一定会实现自己的目标!加油!创作不易,辛苦各位小伙伴们动动小手,三连一波💕💕~~~,本文中也有不足之处,欢迎各位随时私信点评指正!
本节课的代码已上传gitee仓库

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

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

相关文章

关于将Leetcode上代码直接复制到自己环境中的问题

实例代码&#xff08;不考虑代码的优劣性&#xff09;&#xff1a; 注&#xff1a;我用的是一个在线平台的编译器 Problem1 NameError&#xff1a;name ‘List’ is not defined 解决方法&#xff1a;添加下面代码&#xff1a; from typing import ListProblem2 TypeError…

Android Glide预加载preload ,kotlin

Android Glide预加载preload ,kotlin val imageView findViewById<ImageView>(R.id.image_view)Glide.with(this).asBitmap().load(image_file.path).signature(ObjectKey(image_file.path)).addListener(object : RequestListener<Bitmap> {override fun onLoadF…

抓取唯美图库(BeautifulSoup)

使用BeautifulSoup 1、拿到主页面的源代码&#xff0c;然后提取到子页面的简介地址&#xff0c;href 2、通过href拿到子页面的内容。从子页面中找到导图片的下载地址 img -> src 3、下载图片 import requests from bs4 import BeautifulSoupurlhttps://www.umei.cc/bizhi…

qt 闹钟实现

实现一个闹钟 自定义时间 按下开始后 开始计时&#xff0c;结束计时会播报语音 widget.h #ifndef WIDGET_H #define WIDGET_H#include <QWidget> #include <QTimer> #include <QTimerEvent> #include <QDateTime> #include <QTime> #include …

【Leetcode】37.解数独(困难)

一、题目 1、题目描述 编写一个程序,通过填充空格来解决数独问题。 数独的解法需 遵循如下规则: 数字 1-9 在每一行只能出现一次。 数字 1-9 在每一列只能出现一次。 数字 1-9 在每一个以粗实线分隔的 3x3 宫内只能出现一次。(请参考示例图) 数独部分空格内已填入了数字…

UVM中测试用例的启动

一、命令行指定 ./simv -l simv.log UVM_TESTNAMEmy_case0 其中 UVM_TESTNAMEmy_case0 中的my_case0就是测试用例的名字 二、文件结构 在test文件夹中my_case0.sv对应case的名字 三、文件说明 3.1 my_case0.sv 其中其他都是写死的&#xff0c;只有红框的部分根据具体的代码…

温度反转效应Temperature Inversion(载流子迁移率与过阈值电压 谁占主导)

In general, as temperature increases, the delay of standard cells increases because of mobility degradation at higher temperatures.    But in lower technology nodes the impact of temperature on the delay of the cell is inverse. In lower nodes, the delay …

上海亚商投顾:沪指维持震荡 光伏等新能源赛道走强

上海亚商投顾前言&#xff1a;无惧大盘涨跌&#xff0c;解密龙虎榜资金&#xff0c;跟踪一线游资和机构资金动向&#xff0c;识别短期热点和强势个股。 市场情绪 大小指数今日集体反弹&#xff0c;沪指高开后维持震荡走势&#xff0c;创业板指盘中涨超2%&#xff0c;午后涨幅有…

ts开发npm依赖包(插件)demo

序&#xff1a;涉及如下几个点 1、用js开发依赖包&#xff08;换个说法&#xff1a;你在开发第三方的\node_modules的插件了&#xff09; 2、用ts开发依赖包 3、解决本地开发的npm包重命名后不生效的问题 一、js版&#xff08;简单的&#xff09; 你直接在你的项目的\node_mod…

ndp48-web.exe_ndp48-x86-x64-allos-enu.exe_ndp48-x86-x64-allos-chs.exe下载地址

ndp48-web.exe、ndp48-x86-x64-allos-enu.exe、ndp48-x86-x64-allos-chs.exe下载地址 我发现网上几乎找不到地方直接下载&#xff0c;费了我九牛二虎的搜商&#xff0c;才发现原来可以这么下载。 我们可以去微软官方地址下载&#xff1a;下载 .NET Framework 4.8

react 利用antd-mobile实现楼层效果

首先是js模块 import React, { useEffect, useRef, useState } from react import { SideBar } from antd-mobile import ./louceng.css import { useThrottleFn } from ahooksconst items [{ key: 1, title: 第一项, text: <div>12313212313第一项12313212313第一项1…

图床项目之公网发布和测试

项目发布和测试 一、http服务测试1.1、ab http压力测试1.2、post测试&#xff08;注册请求和登录请求&#xff09; 二、性能测试2.1、生成测试脚本2.2、上传测试2.2.1、单客户端测试本地上传到本机服务器2.2.2、如果使用集群的方式进行测试 2.3、下载测试2.4、删除测试2.5、测试…

Vivado2018.3安装教程

1 下载安装包 这个软件是免费的&#xff0c;去官网注册即可完成下载。 https://www.xilinx.com/support/download/index.html/content/xilinx/en/downloadNav/vivado-design-tools/archive.html 2 解压安装包 注意&#xff1a;安装包需要解压到一个全英文路径 3 安装 1.双…

linux与windows趣味谈

文章目录 前言linux&windows两者常见的系统版本系统安装版本选择linux和windows使用体验聊聊折腾收获Linux系统使用技巧 前言 windows和linux同为常见的操作系统&#xff0c;相信大部分人对widows比较熟悉一点&#xff0c;对linux比较陌生一点儿。但相信&#xff0c;作为程…

如何利用MES管理系统做到车间可视化管理

车间可视化管理是提高生产效率和质量的关键一环。而MES生产管理系统能够为企业提供车间实时数据监控、生产计划管理、异常处理等功能&#xff0c;帮助企业实现车间可视化管理。本文将介绍如何利用MES生产管理系统做到车间可视化管理&#xff0c;包括数据采集、数据分析、实时监…

Error:java: 不再支持源选项 5 请使用 6 或更高版本。

今天电脑重新安装系统&#xff0c;安装jdk环境选择了11版本&#xff0c;但是创建工程时突然报错 报错&#xff1a;Error:java: 不再支持源选项 5 请使用 6 或更高版本 解决方案&#xff1a; 1.查看project setting中的project 和Modules的版本号是否与本机jdk的版本号是否一…

基于单片机快递柜的设计与实现

功能介绍 以51单片机作为主控系统&#xff1b;液晶显示当前信息&#xff0c;最多可存储几十个&#xff1b;按下存储按键液晶显示当前快递柜剩余数量&#xff1b;继电器打开&#xff0c;表示用来放物品&#xff1b;正次按下存储按键将取消存快递&#xff0c;继电器关闭快递柜可用…

Linux 生成加密zip文件

一般在Winodws中对zip或者 rar等压缩包文件加密&#xff0c;都是由第三方软件提供的&#xff0c;大家一般右键选择加密就完事了&#xff0c;那么在Linux中&#xff0c;我们如果也有这个需求怎么来操作呢&#xff1f; 实际上&#xff0c;在Linux中这种需求也是挺多的&#xff0c…

WAIC2023圆满落幕!英码科技品牌备受行业青睐,助推人工智能创新发展

7月6日-8日&#xff0c;在美丽的黄浦江畔——上海隆重举办了2023世界人工智能大会&#xff08;以下简称&#xff1a;WAIC2023&#xff09;&#xff0c;英码携人工智能创新产品和行业解决方案精彩亮相&#xff0c;并与广大同仁展开积极交流&#xff0c;共同探讨人工智能发展新技…

【GeoDa实用技巧100例】002:初始GeoDa软件

文章目录 一、GeoDa简介二、软件界面三、新建保存打开数据源1. 新建数据源2. 保存数据源3. 关闭数据源4. 打开数据源 四、保存打开项目1. 保存项目2.打开项目 一、GeoDa简介 GeoDa是一款免费的开源软件工具&#xff0c;用于空间数据分析。GeoDa旨在通过探索和建模空间模式来促…