数组?NO 系Vector啊!

news2025/1/11 21:08:50

文章目录

  • 前言
  • 一、vector的介绍
  • 二、vector的使用
    • 2.1 vector求容量的用法
    • 2.2 vector的增删查改用法
      • 2.2.1 尾插
      • 2.2.2 尾删
      • 2.2.3 头插
      • 2.2.4 任意位置删除
    • 2.3 vector的iterator是什么以及失效问题
  • 三、vector的模拟实现
    • 3.1 成员变量
    • 3.2 成员函数
      • 3.2.1 构造函数
      • 3.2.2 拷贝构造
      • 3.2.3 析构函数
      • 3.2.4 operator=
      • 3.2.5 size
      • 3.2.6 capacity
      • 3.2.7 迭代器相关
      • 3.2.8 reserve
      • 3.2.9 resize
      • 3.2.10 operator[ ]
      • 3.2.11 insert
      • 3.2.12 erase
      • 3.2.13 pop_back
  • 总结


前言

哈喽大家好,这里是夏目学长的C++学习笔记,本次主要讲解一下C++的STL当中的vector
如果大家正在学习vector或者需要对STL更加精进的同学,那么本篇博客非常适合你的学习,因为这里夏目将会不仅仅会写vector的各种功能和还会手搓vector的各种基本功能,帮助大家的学习。


一、vector的介绍

首先如果要学习STL的话肯定要去下面这个网站去读取第一手文档的,这是我们之后学习很重要的手段。

点击这里前去网站:C++学习网站

在这里插入图片描述

这里就需要我们去翻译文本来进行学习,这里节约大家的时间,我就直接归纳总结了。

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

这里我要给大家挖一个坑,因为动态增长这里Linux环境和VS环境的增长倍数是不一样的。这个比较细节,曾经出过面试题。

二、vector的使用

我相信,你读取了上边六条vector的介绍肯定对于vector的了解肯定还是不是很深入,所以我打算从上层应用的角度讲起,然后先学会使用vector,然后再去讲解vector的底层,也就是手挫vector的各种功能写成代码的形式。

所以下面一起聊聊vector的使用,讲到vector其实可以近似理解它就是强化版本的数组,但是数组是静态的,而vector是动态的,vector可以动态增长,增长的倍数在Linux下是标准的2倍,在VS环境下是1.5倍(后面专门验证),数组和vector都可以通过下标的方法进行索引访问,并且他们分配的物理或者逻辑空间是连续的。

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

说了这么多的vector和数组的对比,那既然学习vector了,那么vector肯定要比数组要厉害,所以厉害在哪里?所以,就引出了vector的功能,增删查改

这里举出vector 类型的例子帮助大家去理解。

2.1 vector求容量的用法

在这里插入图片描述
这些都是vector的求容量的用法,这里给出代码,看到代码基本就会使用,我也会添加注释讲解。

#include<iostream>
#include<vector>

using namespace std;
int main()
{
//	test_vector1();
	vector<int> v;
	cout << v.size() << endl;
	cout << v.max_size() << endl;
	cout << v.capacity() << endl; 
	return 0;
}

在这里插入图片描述
size顾名思义就是求vector的已经添加了多少个元素了,capacity就是求vector的总容量是多少,max_size()说白了就是纯纯没用的库函数,所以不学也罢。

2.2 vector的增删查改用法

2.2.1 尾插

vector的尾插,查阅文档就是std::vector::push_back(const T& x);

在这里插入图片描述
顾名思义就是在vector容器里面的末尾插入一个数据。
代码例子:

#include<iostream>
#include<vector>

using namespace std;

void test_vector1()
{
	vector<int> v;
	v.push_back(1);
	v.push_back(2);
	for(int i = 0 ; i < 2 ; ++ i)
		cout << v[i] << " ";
	cout << endl;
}
int main()
{
	test_vector1();
	return 0;
}

例子结果:
在这里插入图片描述
声明: 后面的代码我自动只写类似于test_vectorx();的代码了,这样更加方便,不会冗余。

2.2.2 尾删

vector的尾删,查阅文档就是std::vector::pop_back();
在这里插入图片描述

代码例子:

void test_vector2()
{
	vector<int> v;
	v.push_back(1);
	v.push_back(2);
	v.pop_back();
	for(int i = 0 ; i < v.size() ; ++ i)
		cout << v[i] << " ";
	cout << endl;
}

例子结果:
在这里插入图片描述

2.2.3 头插

vector的头插,查阅文档就是iterator insert(iterator pos, const T& x = T()); 或者就是
void insert(iteraotr pos, size_t n, const T& x)
再或者就是需要用到迭代器了,这个我会专门在下面讲。

在这里插入图片描述

代码例子:

void test_vector3() 
{
	vector<int> v;
	v.push_back(1);
	v.push_back(2);
	
	v.insert(v.begin(),0);//第一种写法
	v.insert(v.begin(),2);
	v.insert(v.begin(),10,1);//第二种写法
	for(int i = 0 ; i < v.size() ; ++ i)
		cout << v[i] << " ";
	cout << endl;
}

测试结果是:1 1 1 1 1 1 1 1 1 1 2 0 1 2

2.2.4 任意位置删除

vector的头插,查阅文档就是iterator erase(iterator pos); 或者就是
iterator erase(iterator first,iterator last);
在这里插入图片描述
代码例子:

void test_vector4() 
{
	vector<int> v;
	v.push_back(1);
	v.push_back(2);
	
	v.insert(v.begin(),0);
	v.insert(v.begin(),1);
	v.insert(v.begin(),2);
	v.insert(v.begin(),3);
	
	for(int i = 0 ; i < v.size() ; ++ i)
		cout << v[i] << " ";
	cout << endl;
	
	v.erase(v.begin());
	v.erase(v.begin() + 2);
	
	for(int i = 0 ; i < v.size() ; ++ i)
		cout << v[i] << " ";
	cout << endl;
}

测试结果是:

删除前:3 2 1 0 1 2
删除后:2 1 1 2

2.3 vector的iterator是什么以及失效问题

在这里插入图片描述

查阅了给出网站的文档我知道了,iterator其实就是vector的迭代器,我是这样理解的,迭代器其实就是类似指针的东西,但是他不是指针,我们在以后的学习还是会继续学习他的,所以这里就来讲解一下iterator的用法。

顾名思义:begin其实就是元素首地址,end就是元素的末地址的下一个地址,rbegin其实就是end,rend就是begin,目前来说这些都是最长用的也是最有用的,剩下的自己去读取文档学习。

然后再讲解一个很重要的知识点就是迭代器失效问题:

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

对于vector可能会导致其迭代器失效的操作有:

  1. 会引起其底层空间改变的操作,都有可能是迭代器失效,比如:resize、reserve、insert、assign、
    push_back等。
  2. 指定位置元素的删除操作–erase
  3. 注意:Linux下,g++编译器对迭代器失效的检测并不是非常严格,处理也没有vs下极端。
  4. 与vector类似,string在插入+扩容操作+erase之后,迭代器也会失效

对于迭代器失效的第一条:
我认为,其实就是it这个指针原本指向老空间没错,但是经过增加元素,导致了vector的扩容,就会使得vector开辟新的一段空间并且把老的空间的内容拷贝进去,而我们的it迭代器,还指向旧空间,因此,就会导致it的失效。
迭代器失效解决办法:在使用前,对迭代器重新赋值即可

对于迭代器失效的第二条:

void test_vector5()
{
	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(),3);
	v.erase(pos);
	cout << *pos << endl;
}

erase删除pos位置元素后,pos位置之后的元素会往前搬移,没有导致底层空间的改变,理论上讲迭代
器不应该会失效,但是:如果pos刚好是最后一个元素,删完之后pos刚好是end的位置,而end位置是
没有元素的,那么pos就失效了。因此删除vector中任意位置上元素时,vs就认为该位置迭代器失效
了。
所以迭代器失效的最重要的方法就是:
迭代器失效解决办法:在使用前,对迭代器重新赋值即可
迭代器失效解决办法:在使用前,对迭代器重新赋值即可
迭代器失效解决办法:在使用前,对迭代器重新赋值即可
重要的事情说三遍。

三、vector的模拟实现

3.1 成员变量

namespace Vct
{
	template<class T>
	class vector
	{
	public:
		typedef T* iterator;
		typedef T* const_iterator;
	private:
		iterator _start;
		iterator _finish;
		iterator _endofstorage;
	}
}

3.2 成员函数

3.2.1 构造函数

vector()
	:_start(NULL)
	,_finish(NULL)
	,_endofstorage(NULL)
{}
		
vector(size_t n, const T& x = T())
	:_start(NULL)
	,_finish(NULL)
	,_endofstorage(NULL)
{
	resize(n,x);
}
		
vector(int n, const T& x = T())
	:_start(NULL)
	,_finish(NULL)
	,_endofstorage(NULL)
{
	resize(n,x);
}	
//迭代器区间初始化 [first,last)  
template<class InputIterator>
vector(InputIterator first, InputIterator last)
{
	while (first != last)
	{
		push_back(*first);
		first++;
	}
}

3.2.2 拷贝构造

//方案一
vector(const vector<T>& V)
	:_start(nullptr)
	, _finish(nullptr)
	, _end_of_storage(nullptr)
{
	iterator tmp = new T[V.capacity()];
	//memcpy(tmp, V._start, sizeof(T) * V.size());
	for (size_t i = 0; i < V.size(); i++)
	{
		tmp[i] = V._start[i];
	}
	_start = tmp;
	_finish = _start + V.size();
	_end_of_storage = _start + V.capacity();
}

//方案二
vector(const vector<T>& V)
	:_start(nullptr)
	, _finish(nullptr)
	, _end_of_storage(nullptr)
{
	reserve(V.capacity());
	for (auto e : V)
	{
		push_back(e);
	}
}

3.2.3 析构函数

~vector()
{
	if(_start)
	{
		delete[] _start;
		_start = _finish = _endofstorage = NULL;
	}
}

3.2.4 operator=

void swap(vector<T> v)
{
	std::swap(v._start, _start);
	std::swap(v._finish, _finish);
	std::swap(v._end_of_storage, _endofstorage);
}

vector<T>& operator=(vector<T> v)//调用拷贝构造函数
{
	swap(v);
	return *this;
}

3.2.5 size

size_t size() const
{
	return _finish - _start;
}

3.2.6 capacity

size_t capacity() const
{
	return _endofstorage - _start;
}

3.2.7 迭代器相关

iterator begin()
{
	return _start;
}

iterator end()
{
	return _finish;
}

const_iterator begin() const
{
	return _start;
}

const_iterator end() const
{
	return _finish;
}

3.2.8 reserve

void reserve(size_t new_capacity)
{
	if (new_capacity > capacity())
	{
		iterator tmp = new T[new_capacity];
		if (_start)//如果原来的_start申请过空间,要先将源空间中的内容拷贝过来
		{
			memcpy(tmp, _start, sizeof(T)*size());
			delete[] _start;
		}

		size_t vsize = size();

		_start = tmp;
		_finish = tmp + vsize;//记得更新_finish
		_endofstorage  = _start + new_capacity;
	}
}

3.2.9 resize

void resize(size_t n, const T& val = T())//缺省参数给的是一个匿名对象
{
	if (n > size())
	{
		//检查容量,扩容
		if (n > capacity())
		{
			reserve(n);
		}

		//开始填数
		iterator it = end();
		while (it < _start + n)
		{
			*it = val;
			it++;
		}

	}

	_finish = _start + n;
}

3.2.10 operator[ ]

T& operator[](size_t pos)//读写版本
{
	assert(pos < size());
	return _start[pos];
}

const T& operator[](size_t pos) const//只读版本
{
	assert(pos < size());
	return _start[pos];
}

3.2.11 insert

iterator insert(iterator pos, const T& val)
{
	assert(pos >= _start && pos <= _finish);
	size_t rpos = pos - _start;//保存一下pos的相对位置
	//检查容量
	if (_finish + 1 >= _end_of_storage)
	{
		size_t new_capacity = capacity();
		reserve(new_capacity == 0 ? 4 : old_capacity * 2);
	}
	pos = _start + rpos;//更新pos
	//插入数据
	iterator end = _finish - 1;
	while (end >= pos)
	{
		*(end + 1) = *end;
		end--;
	}
	*pos = val;
	_finish++;
	return pos;
}

3.2.12 erase

iterator erase(iterator pos)
{
	assert(pos >= _start && pos <= _finish);
	iterator cur = pos + 1;
	while (cur != _finish)
	{
		*(cur - 1) = *cur;
		cur++;
	}
	_finish--;
	return pos;
}

3.2.13 pop_back

//直接复用
void pop_back()
{
	erase(--end());
}

总结

写了一个晚自习终于把C++的vector的博客给写完了,写了很多东西,如果觉得写的不错的可以给夏目一个三连支持一下谢谢。

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

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

相关文章

docker部署elasticsearch+kibana+head

前言 最近&#xff0c;项目需要使用elasticsearch&#xff0c;所以就想快速安装一个使用&#xff0c;最开始是docker安装了7.10.1版本。 后面计划使用Java开发&#xff0c;发现有 RestHighLevelClient 和 Elasticsearch Java API Client两种客户端连接方式。 然后网上查阅了一…

AI为基,快手新商业图景浮现

监制 | 何玺 排版 | 叶媛 快手新商业图景浮现&#xff01; 11月21日&#xff0c;快手发布了2023年Q3财报。该季度内&#xff0c;快手以超2成营收增长的亮眼业绩&#xff0c;展示出强大的经营韧性。同时其在付费短剧、AI应用等业务上的拓展&#xff0c;则让行业和资本市场看到…

无限移动的风景 css3 动画 鼠标移入暂停

<style>*{margin:0;padding:0;/* box-sizing: border-box; */}ul{list-style: none;}#nav{width:900px;height:100px;border:2px solid rgb(70, 69, 69);margin:100px auto; overflow: hidden;}#nav ul{animation:moving 5s linear infinite;width:200%; /*怎么模拟动画…

RUM增强APP端快照配置全量会话回放与自定义协议网络请求采集功能

一直以来&#xff0c;博睿数据秉承着“让每一款软件运行更完美”的产品理念&#xff0c;注重用户体验和反馈&#xff0c;以持续的技术创新&#xff0c;为广大用户提供轻盈、有序、精准的IT运维一体化智能可观测平台&#xff0c;降低运维成本。 近期&#xff0c;博睿数据根据一…

企业如何做好合规管理?

近年来“合规”作为一个热点话题&#xff0c;频繁出现在公众视野&#xff0c;已然成为企业管理发展的大趋势。国家相继出台的各项合规管理标准预示着我国的企业合规管理正逐步从头部央企向民营企业扩展。因此&#xff0c;各大企业将合规管理作为了企业管理的首要任务。 随着中…

hadoop-3.3.5安装过程

准备资源三台虚拟机&#xff1a; 1&#xff09;准备3台服务器&#xff08;关闭防火墙、静态IP、主机名称&#xff09; 2&#xff09;安装JDK 3&#xff09;配置环境变量 4&#xff09;安装Hadoop 5&#xff09;配置环境变量 安装虚拟机&#xff08;略&#xff09;--1台即…

第二证券:趋势线是画最低点还是收盘价?

趋势线是股票分析中最底子的技术指标之一。趋势线是一种可帮忙股票生意者辨认价格趋势的图形方法。趋势线是可以经过联接恣意两个价格点画出的一条直线。但是&#xff0c;在画出趋势线时&#xff0c;一个常见的问题是&#xff0c;运用最低点还是收盘价来画趋势线&#xff1f;在…

ROS话题消息实时展示在WEB网页上

【使用背景】 最近公司搞了一个室外无人车的项目&#xff0c;需要用到GPS组合惯导&#xff0c;但是这套传感器由于成本控制&#xff0c;它没有提供小程序或是APP之类的数据监测手段&#xff0c;只能通过一个Windows上位机软件去看GPS实时数据&#xff0c;这对于单人外场调试来…

【Skynet 入门实战练习】分布式 ID | 雪花算法 | 缓存设计 | LRU算法 | 数据库

文章目录 前言雪花算法LRU 算法缓存模块数据库测试逻辑 前言 本节实现了 分布式 ID 生成系统&#xff0c;采用雪花算法实现唯一 ID&#xff1b;实现缓存架构&#xff0c;采用 LRU &#xff08;最近最少使用&#xff09;算法。 雪花算法 分布式 ID 生成算法的有很多种&#x…

c#学习相关系列之as和is的相关用法

一、子类和父类的关系 public class Program{static void Main(string[] args){Animal animal new Dog();// Dog dog (Dog)new Animal(); 编译成功&#xff0c;运行报错Dog dog (Dog)animal;Dog dog new Dog();Animal animal dog; //等价于Animal animal new Dog();}}pub…

最新UI酒桌喝酒游戏小程序源码,直接上传源码到开发者端即可,带流量主

源码介绍&#xff1a; 2023最新UI酒桌喝酒游戏小程序源码 娱乐小程序源码 带流量主.修改增加了广告位&#xff0c;直接上传源码到开发者端即可。 通过后改广告代码&#xff0c;然后关闭广告展示提交&#xff0c;通过后打开即可。无广告引流。 流量主版本的&#xff08;配合流…

gitlab高级功能之CI/CD组件 - 实践(二)

上一篇主要讲解了CI/CD组件的原理&#xff0c;看起来稍微有一点枯燥&#xff0c;那么接下来给大家演示下如何使用。 案例 创建一个项目&#xff08;README.md&#xff0c;template目录&#xff09; 案例1 step1: 在template中新建yml文件&#xff0c;cat templates/test-st…

计算机毕业设计|基于SpringBoot+SSM+MyBatis框架的迷你仿天猫商城购物系统设计与实现

计算机毕业设计|基于SpringBoot+MyBatis框架的仿天猫商城购物系统设计与实现 迷你仿天猫商城是一个基于SpringBoot+SSM+MyBatis框架的综合性B2C电商平台,需求设计主要参考天猫商城的购物流程:用户从注册开始,到完成登录,浏览商品,加入购物车,进行下单,确认收货,评价等…

分布式任务调度系统XXL-Job

1.介绍 XXL-JOB是一个分布式任务调度平台&#xff0c;其核心设计目标是开发迅速、学习简单、轻量级、易扩展。 执行流程&#xff1a; a、执行器根据配置的调度中心的地址&#xff0c;自动注册到调度中心 b、达到任务触发条件&#xff0c;调度中心下发任务 c、执行器基于线程池…

进程(process) vs 线程(Thread)

文章目录 前言一、进程&#xff08;process) vs 线程&#xff08;Thread&#xff09;引用自维基百科引用自CSDN INCOE AI引用自 geeksforgeeksOS( Operating System )如何调度线程的线程锁的核心原理是什么? 总结 前言 &#x1f680; 多方面理解进程(process) &#xff0c;线…

gateway网关一直404问题

1.nacos配置管理->配置管理列表-> 有gateway网关的相关配置文件 2. idea项目中添加jar包 3.配置bootstarp.yaml 4. 通过网关访问路径&#xff1a; ip网关端口网关配置服务名字接口

SpringBootWeb案例_02

Web后端开发_05 SpringBootWeb案例_02 1.新增员工 1.1需求 在新增用户时&#xff0c;我们需要保存用户的基本信息&#xff0c;并且还需要上传的员工的图片&#xff0c;目前我们先完成第一步操作&#xff0c;保存用户的基本信息。 1.2 接口文档 基本信息 请求路径&#xff…

前端入门(五)Vue3与TypeScript

文章目录 Vue3简介创建Vue3工程使用vite创建vue-cli方式 常用 Composition API安置项 - setupsetup的执行时机与参数 响应式原理vue2中的响应式vue3中的响应式ref函数reactive函数reactive与ref对比 Vue3简介 Vue3带来了&#xff1a; 1、性能的提升&#xff1a; 打包大小减少…

EDA实验-----正弦信号发生器的设计(Quartus II )

目录 一、实验目的 二、实验仪器 三、实验原理 四、实验内容 五、实验步骤 六、注意事项 七、实验过程&#xff08;操作过程&#xff09; 1.定制LPM_ROM模块 2.定制LPM_ROM元件 3.计数器定制 4.创建锁相环 5.作出电路图 6.顶层设计仿真 一、实验目的 学习使用Ver…

不同领域文章一键采集,全网关键词文章采集工具

不同领域的从业者、学生、研究者等都可能需要大量的文章来支持他们的工作和学术研究。然而&#xff0c;手动搜索和整理这些文章费时费力&#xff0c;这个时间大家就会借助全网文章采集工具&#xff0c;只需要输入关键词&#xff0c;就能够采集大量相关文章&#xff0c;为大家提…