【C++】vector的底层原理讲解及其实现

news2024/11/15 20:55:31

目录

一、认识vector底层结构
二、初始化vector的函数

  1. 构造函数
  2. 拷贝构造
  3. 赋值构造
  4. initializer_list构造
  5. 迭代器区间构造

三、迭代器
四、数据的访问
五、容量相关的函数
六、关于数据的增删查改操作


一、认识vector底层结构
STL库中实现vector其实是用三个指针来完成的,使用这三个指针(或称为迭代器)变量来管理其内存

在这里插入图片描述
其实vector和string的实现非常相似,都是利用了顺序表结构,在vector的实现上我们遵循底层用三个指针来完成,_statr,_finish,_end_fo_storage分别指向顺序表的头,顺序表存储数据的有效个数的位置,顺序表的结束

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

二、初始化vector的函数

1、构造函数
①最常用的无参默认构造
vector();

vector()
:_strat(nullptr)
,_finish(nullptr)
,_endofstorage(nullptr)
{}

这个很简单,我就不多讲了,后面遇到难点我会详细说明,便于大家理解

②用n个val构造一个vector
explicit vector (size_type n, const value_type& val = value_type();
库里面用explicit修饰构造函数,是为了防止构造函数发生隐式类型转换

vector(size_t n, const T& val = T())
{
	_start = new T[n];
	_finish = _start;
	while (_finish!=_start+n)
	{
		*_finish = val;
		_finish++;
	}
	_endofstorage = _start + n;
}

2、拷贝构造
vector(const vector& v);
拷贝构造:用一个已经存在的对象来初始化另一个正在创建的对象

vector(const vector& v)
{
	_start = new T[v.size()];
	memcpy(_start, v._start, sizeof(T) * v.size());
	_finish = _start + v.size();
	_endofstorage = _finish;
}

3、赋值构造
赋值构造:两个已经存在的对象,一个赋值给另一个
vector& operator= (const vector& v);

void swap(vector& v)
{
	std::swap(_start, v._start);
	std::swap(_finish, v._finish);
	std::swap(_endofstorage, v._endofstorage);
}
//v1=v2;
vector& operator= (vector v)
{
	swap(v);
	return *this;
}

这里我利用库里的函数来实现swap,只交换vector的三个指针更高效,赋值构造的参数是传值传参,会调用拷贝构造,将v2拷贝一份给v,然后之间调用swap函数,将v和this交换,最后返回this即可

4、initializer_list构造
vector (initializer_list<T> il);
tips:这里的initializer_list实际是个类,C++底层将其封装了,里面也有begin,end,size

//vector<int> v={1,2,3,4,5};
vector(initializer_list<T> il)
{
	for (auto e : il)
	{
		push_back(e);
	}
}

5、迭代器区间构造
template <class InputIterator> vector(InputIterator first, InputIterator last);

// 类模板的成员函数可以是函数模板
template <class InputIterator>
vector(InputIterator first, InputIterator last)
{
	while (first != last)
	{
		push_back(*first);
		++first;
	}
}

注意:如果加了迭代器区间构造会造成一个问题,就是在调用时和vector(size_t n, const T& val = T())会出现冲突,底层给出的解决方案就是加一个重载vector(int n, const T& val = T())

三、迭代器
这里博主就只介绍最重要的迭代器部分
template<class T>
class vector
	{
	public:
		typedef T* iterator;
		typedef const T* const_iterator;
		iterator begin()
		{
			return _start;
		}
		iterator end()
		{
			return _finish;
		}
		const_iterator begin() const//加了const修饰,const对象可以调用,非const对象也可以调用
		{
			return _start;
		}
		const_iterator end() const
		{
			return _finish;
		}
	private:
		iterator _start; //指向空间(顺序表)的开始
		iterator _finish;//指向有效数据个数的位置
		iterator _endofstorage;//指向空间的结束
};


四、数据的访问

由于vector底层是连续的物理空间,所以支持下标访问
T& operator[] (size_t n);
const T& operator[] (size_t n) const;

T& opTerator[](size_t n)
{
	assert(n < size());
	return _start[n];
}
const T& operator[](size_t n) const
{
	assert(n < size());
	return _start[n];
}

五、容量相关的函数

size(有效数据个数)和capacity(空间容量大小)

size_t size() const
{
	return _finish - _start;//指针减指针得到两个指针之间的数据个数
}
size_t capacity() const
{
	return _endofstorage - _start;
}

reserve
void reserve (size_t n);
易错点1
在这里插入图片描述

void reserve(size_t n)
{
	if (n > capacity())
	{
		size_t old_size = size();
		T* temp = new T[n];
		memcpy(temp, _start, sizeof(T) * size());
		delete[]_start;
		_start = temp;
		_finish = _start + old_size;
		_endofstorage = _start+n;
	}
}

改成现在这个样子确实是解决了上图中的问题,但是这个就是对的吗?让我们一起来看一下这个代码究竟对不对

易错点2
其实上面的代码大体逻辑是没有什么问题的,问题就出在这个memcpy上,要知道memcpy底层实现是按字节一个一个拷贝过去的,虽然我们的vector是深拷贝,但是memcpy是浅拷贝,这样写针对内置类型是🆗的,但是针对自定义类型会存在指向同一块空间的问题,两次析构等问题,话不多说,我们看图解

出错案例:
在这里插入图片描述
出错图解:
在这里插入图片描述
其实要改正这个问题有一个很简单的方式,采用赋值的方式,无论是内置类型还是自定义类型,在赋值时都会调用他的拷贝构造,这样就自动调用该类型的深拷贝了

void reserve(size_t n)
{
	if (n > capacity())
	{
		size_t old_size = size();
		T* temp = new T[n];
		//memcpy(temp, _start, sizeof(T) * size());
		for (size_t i = 0; i < old_size; i++)
		{
			temp[i] = _start[i];
		}
		delete[]_start;
		_start = temp;
		_finish = _start + old_size;
		_endofstorage = _start+n;
	}
}

resize
void resize (size_t n, const T& val=T());

void resize(size_t n, const T& val=T())
{
	if (n > capacity())
	{
		reserve(n);
		for (size_t i = size(); i < n; i++)
		{
			_start[i] = val;
		}
		_finish = _start + n;
	}
	else
	{
		_finish = _start + n;
	}
}

注意:reverseresize都不会缩容

empty
bool empty() const

bool empty()const
{
	return _finsh == _start;
}

六、关于数据的增删查改操作

push_back
void push_back (const T& val);

void push_back(const T& val)
{
	if (size() == capacity())
	{
		reserve(capacity() == 0 ? 4 : 2 * capacity());
	}
	*_finish = val;
	_finish++;
}

inserrt
void insert (iterator pos, const T& val);

void insert(iterator pos, const T& val)
{
	assert(pos>=_start);
	assert(pos <= _finish);
	size_t d = pos - _start;//先记下pos和_start的相对位置
	if (size() == capacity())
	{
		reserve(capacity() == 0 ? 4 : 2 * capacity());
		//如果扩容了,要更新pos
		pos = _start + d;
	}
	iterator end = _finish;
	while (pos < end)
	{
		*end = *(end - 1);
		end--;
	}
	*pos = val;
	_finish++;
}

erase
iterator erase (iterator pos);

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

clear
void clear();

void clear()
{
	_finish = _start;
}

vector章节我们就到这里啦,欢迎大家来学习指教下一篇list章节😘

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

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

相关文章

PY32F403系列单片机,32位M4内核MCU,主频最高144MHZ

PY32F403系列单片机是基于Arm Cortex-M4核的32位通用微控制器产品。内置的FPU和DSP功能支持浮点运算和全部DSP指令。通过平衡成本&#xff0c;性能&#xff0c;功耗来获得更好的用户体验。 PY32F403单片机典型工作频率可达144MHZ&#xff0c;内置高速存储器&#xff0c;丰富的…

sql注入之bool盲注

目录 盲注步骤 1、进入靶场 2、如下图所示输入&#xff1f;id1‘ 判断此时存在注入点 3、判断列数 ​编辑 4、开始盲注 普通的python脚本 代码思想 结果 二分查找python脚本 二分查找算法思想简介 二分查找与普通查找的主要差距 代码思想 代码 结果​编辑 下面以…

图像融合-下游任务(目标检测、实例分割、深度估计、局部区域细节放大)

下游任务: 采用目标检测、实例分割和深度估计的下游任务来验证图像融合结果质量。 文章目录 下游任务:1.目标检测2.实例分割3.深度估计局部细节放大工具Update1.目标检测 YOLOv8:https://github.com/ultralytics/ultralytics 步骤内容第一步下载项目到本地第二步安装READ…

文献阅读——中国农产品期货的正负价格泡沫(LPPLS)

Positive and negative price bubbles of Chinese agricultural commodity futures Fang, Ming, Yizhou Lin, and Chiu-Lan Chang. “Positive and negative price bubbles of Chinese agricultural commodity futures.” Economic Analysis and Policy 78 (2023): 456-471. 经…

以项目为中心打造企业卓越经营管理体系︱PMO大会

全国PMO专业人士年度盛会 广东林氏家居股份有限公司战略副总裁杨永柠先生受邀为PMO评论主办的2024第十三届中国PMO大会演讲嘉宾&#xff0c;演讲议题为“以项目为中心打造企业卓越经营管理体系”。大会将于6月29-30日在北京举办&#xff0c;敬请关注&#xff01; 议题简要&…

WPS表格:使用vlookup函数解决乱序数据对应问题

我们常常会遇到两个表格的内容相同&#xff0c;但是顺序不一致的情况。并且这种顺序无关于简单的排序&#xff0c;而是一种业务性很强的复杂排序规则。下面我举个例子&#xff0c;使用VLOOKUP复制数据。 假设太阳系行星举办了一次卖萌比赛&#xff0c;由太阳妈妈决定谁是最萌的…

【JVM】ASM开发

认识ASM ASM是一个Java字节码操纵框架&#xff0c;它能被用来动态生成类或者增强既有类的功能。 ASM可以直接产生二进制class文件&#xff0c;也可以在类被加载入虚拟机之前动态改变类行为&#xff0c;ASM从类文件中读入信息后能够改变类行为&#xff0c;分析类信息&#xff…

【Unity 鼠标输入检测】

Unity 鼠标输入检测 Unity提供了多种方法来检测和处理鼠标输入&#xff0c;允许开发者在游戏中实现对鼠标移动、点击和滚轮滚动的响应。以下是一些基本的鼠标输入检测方法&#xff1a; 1. Input.mousePosition 这个属性返回当前鼠标指针的屏幕坐标。坐标是以像素为单位的&…

JVM调优-调优原则和原理分析

1.写在前面 对于JVM调优这个话题&#xff0c;可能大部分程序员都听过这个名词。 但是绝大多数程序员&#xff0c;都没有真真实实去干过&#xff0c;都没有真实的实践过。也不懂得如何调优&#xff1f;不知道要调成怎么样&#xff1f; 那今天咋们就对这个话题来展开描述一下&…

IDEA安装使用Git

IDEA安装使用Git 1 Git下载与安装 2 在IDEA中使用Git 2.1 IDEA中配置Git 在IDEA中使用Git&#xff0c;本质上还是使用本地安装的Git软件&#xff0c;所以需要在IDEA中配置Git。 2.2 在IDEA中使用Git 2.2.1 获取Git仓库 在IDEA中使用Git获取仓库有两种方式: 本地初始化仓库从…

软件全套资料梳理(需求、开发、实施、运维、安全、测试、交付、认证、评审、投标等)

软件全套精华资料包清单部分文件列表&#xff1a; 工作安排任务书&#xff0c;可行性分析报告&#xff0c;立项申请审批表&#xff0c;产品需求规格说明书&#xff0c;需求调研计划&#xff0c;用户需求调查单&#xff0c;用户需求说明书&#xff0c;概要设计说明书&#xff0c…

kali linux2024.1版安装

1 基于 VMware 安装 Kali 系统 打开已经安装好的 VMware 程序&#xff0c;点击选项卡中的“主页”--》而后点击“创建新的虚拟机” 选择“典型(推荐)”&#xff0c;并点击“下一步” 客户机操作系统镜像选择&#xff1a;选择“稍后安装操作系统”&#xff0c;并点击“下一步”…

全新神经网络架构KAN——本文用于学习与探索

论文地址&#xff1a;https://arxiv.org/pdf/2404.19756 Github&#xff1a;GitHub - KindXiaoming/pykan: Kolmogorov Arnold Networks 文档说明&#xff1a;Welcome to Kolmogorov Arnold Network (KAN) documentation! — Kolmogorov Arnold Network documentation 本文仅…

2024年4月24日华为春招实习试题【三题】-题目+题解+在线评测,2024.4.24,华为机试

2024年4月24日华为春招实习试题【三题】-题目题解在线评测&#xff0c;2024.4.24&#xff0c;华为机试 &#x1f3e9;题目一描述&#xff1a;输入格式输出格式样例1样例2样例3数据范围解题思路一&#xff1a;dfs解题思路二&#xff1a;直接二分查找哇&#xff01;解题思路三&am…

经典权限五张表功能实现

文章目录 用户模块(未使用框架)查询功能实现步骤代码 新增功能实现步骤代码 修改功能实现步骤代码实现 删除功能实现步骤代码实现 用户模块会了&#xff0c;其他两个模块与其类似 用户模块(未使用框架) 查询功能 这里将模糊查询和分页查询写在一起 实现步骤 前端&#xff1…

Linux下安装gmp6.2.1的详细操作(深度学习)

方式一&#xff1a;编译gmp GMP官方地址https://gmplib.org/ 1. 官网下载gmp安装包 2. 解压下载好的安装包 tar -zxvf gmp-6.2.1.tar.bz2 3. 进入解压后的文件夹 cd gmp-6.2.1 4. 指定安装路径进行安装 # /usr/local换成自己的安装路径 ./configure --prefix/usr/local 5. 编…

Excel快速填充序号的方法

Excel快速填充序号常用的方法。 方法一&#xff1a;填充前面序号后拖拽 特点&#xff1a; 能有规律的填充&#xff0c;排序的行数由拖拽的行数决定。 此方法填充的序号等效于手打的序号&#xff0c;删除一行后下一行不会自动更新排序。 步骤&#xff1a;输入两个初始序号&…

org.springframework.jdbc.BadSqlGrammarException

Cause: java.sql.SQLSyntaxErrorException: Table ‘web.emp’ doesn’t exist 产生原因&#xff1a;web表找不到&#xff0c;所以可能数据库配置错误 spring.datasource.urljdbc:mysql://localhost:3306/web02 更改完成后运行成功

windows@注册表介绍@注册表的查看和编辑操作

文章目录 abstractrefs注册表的主要组件包括根键极其缩写名称&#x1f47a;子键特性 查看注册表&#x1f47a;使用powershell查看路径下的子路径声明概念Get-ChildItem查看注册表路径下的项Set-Location进入注册表路径举例说明查看文件系统某个路径下的项查看某个注册表路径的项…

系统分析师论文——论软件需求分析方法和工具的选用

现已临近2024年软考&#xff0c;周围一些报名参加系统分析师考试的“小伙伴”还未准备论文&#xff0c;我分享早年写的一些内容&#xff08;包括参加继续教育准备的论文&#xff09;&#xff0c;仅供大家结合最新考纲要求酌情参考&#xff0c;希望予人玫瑰&#xff0c;手有余香…