【c++进阶(三)】STL之vector的介绍和使用

news2024/11/30 11:55:07

💓博主CSDN主页::Am心若依旧💓

⏩专栏分类c++从入门到精通⏪
🚚代码仓库:青酒余成🚚

🌹关注我🫵带你学习更多c++
  🔝🔝

 vector的介绍

1.vector表示的是可变序列大小的容器

2、vector就像数组一样,也采用的连续空间来存储元素,这也意味着可以采用下标对vector的元素进行访问。
3、vector与普通数组不同的是,vector的大小是可以动态改变的。
4、当vector需要重新分配大小时,其做法是,分配一个新的数组,然后将全部元素移到这个数组当中,并释放原来的数组空间。---这就表明原来指向该位置的迭代器已经失效了,所以在插入和删除数据的时候要返回一个合法的迭代器的位置。
5、vector分配空间策略:vector会分配一些额外的空间以适应可能的增长,因此存储空间比实际需要的存储空间一般更大。不同的库采用不同的策略权衡空间的使用和重新分配,以至于在末尾插入一个元素的时候是在常数的时间复杂度完成的。
6、由于vector采用连续的空间来存储元素,与其他动态序列容器相比,vector在访问元素的时候更加高效,在其末尾添加和删除元素相对高效,而对于不在其末尾进行的删除和插入操作效率则相对较低。--这是因为不在其末尾进行删除和插入操作时,要整体移动数据,效率较低

vector的使用

        vector的定义方式

方式一:构造一个空容器--刚开始v1数据内部实际有效的大小是0

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

方式二:构造一段含有n个val的容器

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

方式三:用拷贝构造来复制一个一模一样的容器

vector<int> v3(v2);//v3把v2给复制了一份

方式四:用迭代器区间来进行构造

vector <int> v4(v2.begin(),v2.end());//用v2里面的值来构造v4

此外,不仅仅只是可以使用int容器,也可以使用其他数据类型 

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

vector的空间增长问题

在空间增长这一块:主要有resize,reserver,capacity, size,empty等几个重要的函数

size和capacity函数

通过size函数获取当前容器中的有效元素个数,通过capacity函数获取当前容器的最大容量。

vector <int> v(5, 2);
v.push_back(1);
cout << v.size() << endl;//6
cout << v.capacity() << endl;//7

empty函数

主要是判断当前对象中是否还有元素,有元素就返回假,没有元素就返回真

vector <int> v(5, 2);
v.push_back(1);
cout << v.size() << endl;//6
cout << v.capacity() << endl;//7
cout << v.empty() << endl;//0

返回0的含义是当前v对象里面是存在函数的,所以返回的是假,也就是0

resize和reserve函数

reserve函数的使用规则:

1、当所给值大于容器当前的capacity时,将capacity扩大到该值。---扩容的本质就是新开辟一块空间,然后把原空间的数据拷贝过来,然后再释放原空间---这个地方也是导致迭代器失效的本质
2、当所给值小于容器当前的capacity时,什么也不做。

resize函数的使用规则:

 1、当所给值大于容器当前的size时,将size扩大到该值,扩大的元素为第二个所给值,若未给出,则默认为0。
 2、当所给值小于容器当前的size时,将size缩小到该值

vector <int> v(5, 2);
v.reserve(4);//比当前的空间要小,所以不会发生扩容
cout << v.capacity() << endl;//输出依然是7
v.reserve(10);//比当前的capacity大,所以会发生扩容
cout << v.capacity() << endl;//发生了扩容,输出变为了10

v.resize(3);
for (auto e : v)
{
	cout << e << " ";
}
cout << endl;//打印出 2 2 2
v.resize(6);
for (auto e : v)
{
	cout << e << " ";
}
cout << endl;//打印出 2 2 2 0 0 0
v.resize(10,1);
for (auto e : v)
{
	cout << e << " ";
}
cout << endl;//打印出 2 2 2 0 0 0 1 1 1 1

vector的迭代器使用

begin和end

通过begin函数可以得到容器中第一个元素的正向迭代器,通过end函数可以得到容器中最后一个元素的后一个位置的正向迭代器。
正向迭代器遍历容器:

	auto it = v.begin();
	while (it != v.end())
	{
		cout << *it << ' ';
		it++;
	}
	cout << endl;
rbegin和rend

rbegin就指向最后一个元素,rend就指向第一个元素的前一个位置

反向迭代器遍历容器:

	auto it = v.begin();
	while (it != v.end())
	{
		cout << *it << ' ';
		it++;
	}
	cout << endl;//打印2 2 2 0 0 0 0 1 1 1 1
	auto it1 = v.rbegin();
	while (it1 != v.rend())
	{
		cout << *it1 << ' ';
		it1++;
	}
	cout << endl;//打印 1 1 1 1 0 0 0 2 2 2

vector的增删查改

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;
}
pop_back和push_back函数

这就是一个普通的尾插和尾删的函数,就不做过多的解释了。不懂的可以查看下面链接:

vector::push_back - C++ Reference

vector::pop_back - C++ Reference

由于上面的插入和删除都是在某个迭代器的位置进行插入和删除,所以你的先有一个迭代器的位置,而我们目前所拥有的迭代器位置就只有两个 begin()和end(),其中end还是无效的。

所以为了满足在某一特定的值前面进行插入,c++还给我们内置了一个find函数

find函数:
find函数共三个参数,前两个参数确定一个迭代器区间(左闭右开),第三个参数确定所要寻找的值。
find函数在所给迭代器区间寻找第一个匹配的元素,并返回它的迭代器,若未找到,则返回所给的第二个参数。

#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(), 2); //获取值为2的元素的迭代器
	
	v.insert(pos, 10); //在2的位置插入10

	pos = find(v.begin(), v.end(), 3); //获取值为3的元素的迭代器
	
	v.erase(pos); //删除3

	return 0;
}

ps:这个find函数实在algorithm里面的,并不是STL中vector内部的

阅读到这里大部分的vector中的函数接口都已经学完了,如果有兴趣学习更多的参考如下:

vector - C++ Reference

总结与拓展 

vector迭代器失效问题

迭代器的主要作用就是让我们在使用各个容器时不用关心其底层的数据结构,而vector的迭代器在底层实际上就是一个指针。迭代器失效就是指迭代器底层对应指针所指向的空间被销毁了,而指向的是一块已经被释放的空间,如果继续使用已经失效的迭代器,程序可能会崩溃。

那么在什么情况下有可能发生迭代器失效呢? ---在使用insert和erase函数时,最容易发生迭代器失效的问题,因为在插入的过程中,你在某个迭代器位置进行插入的同时进行了扩容,由于扩容的机制,会导致原来的空间进行释放,所以说这就容易导致迭代器失效。

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

 这里就是在扩容的时候,导致的迭代器失效的问题。

解决办法就是:使用迭代器时,永远记住一句话:每次使用前,对迭代器进行重新赋值。就能够完美的解决这个问题了

还有一个就是在删除时,当你删除了该位置的元素,你在对该位置去进行解引用,那么这样就导致你访问了不属于你的资源,那么就会发生程序报错。

#include <iostream>
#include <vector>

int main() {
    std::vector<int> vec = {1, 2, 3, 4, 5};
    std::vector<int>::iterator it = vec.begin() + 1; // 指向第二个元素
    vec.erase(it); // 删除第二个元素
    std::cout << *it; // 试图访问已删除元素的位置,将导致未定义行为
    return 0;
}

 解决办法:每次删除一个元素之后,都给他返回一个合法的迭代器位置,即返回删除位置的下一个位置的迭代器。

                                      下期预告:STL之vector的模拟实现 

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

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

相关文章

MySQL 日志(一)

本篇主要介绍MySQL日志的相关内容。 目录 一、日志简介 常用日志 一般查询日志和慢查询日志的输出形式 日志表 二、一般查询日志 三、慢查询日志 四、错误日志 一、日志简介 常用日志 在MySQL中常用的日志主要有如下几种&#xff1a; 这些日志通常情况下都是关闭的&a…

一文读懂Java线程池之线程复用原理

什么是线程复用 在Java中,我们正常创建线程执行任务,一般都是一条线程绑定一个Runnable执行任务。而Runnable实际只是一个普通接口,真正要执行,则还是利用了Thread类的run方法。这个rurn方法由native本地方法start0进行调用。我们看Thread类的run方法实现 /* What will be…

Mysql8.0.31开启mysqlbinlog

1、查看mysqlbinlog是否已经开启 show variables like %log_bin%; log_bin: ON是OFF否已经开启binlog log_bin_basename: binlog所在路径的文件开头前缀名 lob_bin_index: binlog文件的索引文件所在路径 2、若log_binOFF&#xff0c;则开启log_bin -- 退出mysql client ex…

open-amv开发环境搭建

open-amv是基于rv1103主控芯片的视觉开发板子 1.板子使用 板子使用type c作为调试口&#xff0c;同时供电&#xff0c;请在电脑上下载adb&#xff0c;当板子通过tpye c与电脑连接后&#xff0c;执行命令adb shell就会进入到板子的linux系统命令行。 2.编译环境 2.1 搭建doc…

【网络编程】优雅断开套接字连接

Linux的close函数和Windows的closesocket函数意味着完全断开连接。完全断开不仅指无法传输数据&#xff0c;而且也不能接收数据。 2台主机正在进行双向通信&#xff0c;主机A发送完最后的数据后&#xff0c;调用close函数断开了连接&#xff0c;之后主机A无法再接收主机B传输的…

超全Midjourney自学教程,怒码1万3千字!这是我见过最良心的教程啦!

前段时间&#xff0c;后台有网友私信我&#xff0c;说想跟我一起学AI~当时一边开心一边惶恐&#xff0c;满足于被人看到自己的努力、又担心自己是不是教不好别人&#xff0c;毕竟我自己也是业余时间边学边发的那种~ 不过&#xff0c;我还是会继续搬运或整理一些我认为值得记录…

C++100行超简单系统

非常好用&#xff0c;小白也可以自己修改 先来看图片&#xff1a; 用法附在代码里了&#xff01; #include <bits/stdc.h> #include <windows.h>using namespace std;struct users {string name;string num; bool f; } u[10000];int now_users 0; /*当前用户数*…

MyBatis使用 PageHelper 分页查询插件的详细配置

1. MyBatis使用 PageHelper 分页查询插件的详细配置 文章目录 1. MyBatis使用 PageHelper 分页查询插件的详细配置2. 准备工作3. 使用传统的 limit 关键字进行分页4. PageHelper 插件&#xff08;配置步骤&#xff09;4.1 第一步&#xff1a;引入依赖4.2 第二步&#xff1a;在m…

LDR6020显示器应用:革新连接体验,引领未来显示技术

一、引言 随着科技的飞速发展&#xff0c;显示器作为信息展示的重要载体&#xff0c;其性能和应用场景不断得到拓展。特别是在办公、娱乐以及物联网等领域&#xff0c;用户对显示器的需求越来越多样化。在这一背景下&#xff0c;LDR6020显示器的出现&#xff0c;以其卓越的性能…

【LeetCode:2779. 数组的最大美丽值 + 排序 + 二分】

&#x1f680; 算法题 &#x1f680; &#x1f332; 算法刷题专栏 | 面试必备算法 | 面试高频算法 &#x1f340; &#x1f332; 越难的东西,越要努力坚持&#xff0c;因为它具有很高的价值&#xff0c;算法就是这样✨ &#x1f332; 作者简介&#xff1a;硕风和炜&#xff0c;…

干部管理软件有哪些

随着信息技术的飞速发展&#xff0c;干部管理软件在各级党政机关、国企事业单位中扮演着越来越重要的角色。这些软件通过整合干部管理的各项业务流程&#xff0c;实现了干部信息的系统化、规范化和高效化管理。以下是几款主流的干部管理软件及其特点&#xff1a; 一、干部信息…

C++ 06 之 c++增强

c06c增强.cpp #include <iostream>using namespace std; // 1、全局变量检测增强&#xff1a;可以检测出重定义 (c语言不会报错&#xff0c;但是C会报错) //int a; //int a 10;// 2、函数检测增强: 函数返回值类型、形参类型、实参个数 int sum(int a, int b) {return …

Json-server 的使用教程

目录 前言一、简介二、安装与配置1. 安装 node-js2. npm 镜像设置3. 安装 json-server 三、使用1. 创建本地数据源2. 启动 Json Server3. 操作数据&#xff08;1&#xff09;查询数据&#xff08;2&#xff09;新增数据&#xff08;3&#xff09;修改数据&#xff08;4&#xf…

仿element-ui 实现自己组件库 <3>

目录 input 组件封装 v-model用在组件上 显示和隐藏密码 封装switch组件 实现转换的功能 设置checkbox input 组件封装 首先input组件的基本框架和样式&#xff1a; <div class"miao-input"><input class"miao-input_inner" > </div…

LDR6500:手机电脑拓展坞转接器方案的卓越之选

随着科技的飞速发展&#xff0c;手机和电脑已成为我们日常生活中不可或缺的工具。然而&#xff0c;它们的接口有限&#xff0c;经常难以满足我们多样化的需求。这时&#xff0c;一款高效、稳定的拓展坞转接器就显得尤为重要。LDR6500&#xff0c;作为乐得瑞科技精心研发的USB P…

HCIA12 NAT网络地址转换实验

NAT&#xff08;Network Address Translation&#xff09;是将 IP 报头中的 IP 地址转换为另一个 IP 地址的过程。主要俩好处&#xff1a; • 有效避免来自外网的攻击&#xff0c;可以很大程度上提高网络安全性。 • 控制内网主机访问外网&#xff0c;同时也可以控制外网…

微信鸿蒙版本来了 我不允许你不会

前言: 各位同学大家好, 好久没有更新鸿蒙文章了 现在更新一个鸿蒙版本高仿微信版本 那么废话不多说 我们正式开始 作者:徐庆 团队:坚果派 公众号:“大前端之旅” 润开鸿生态技术专家,华为HDE,CSDN博客专家,CSDN超级个体,CSDN特邀嘉宾,InfoQ签约作者,OpenHarmony布…

SQLServer 借助Navcate做定时备份的脚本

首先创建SQLServer链接&#xff0c;然后在Query标签种创建一个查询 查询内容如下 use ChengYuMES declare ls_time varchar(1000) declare ls_dbname varchar(1000) set ls_time convert(varchar, getdate(), 112) _ replace(convert(varchar, getdate(), 108), :, )-- 需…

51单片机STC89C52RC——代码编译

1&#xff0c;勾选 “Create HEX file” 2&#xff0c;编译

周五美国股市总结,标普止步四日连涨,纳指五日连创新高,法股单周跌幅两年多最深

美国消费者信心意外下滑至七个月新低&#xff0c;通胀预期反弹&#xff0c;标普大盘脱离历史最高&#xff0c;道指连跌四日&#xff0c;罗素小盘股跌至六周新低&#xff0c;有分析称对经济担忧浮现。全周标普和纳指分别累涨1.6%和3.2%&#xff0c;都是八周里第七周上涨&#xf…