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

目录

一、认识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章节😘

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

进入泛型的世界

泛型的理解和好处 泛型的好处 编译时&#xff0c;检查添加元素的类型&#xff0c;提高了安全性减少了类型转换的次数&#xff0c;提高效率 不使用泛型 Dog-加入->Object-取出->Dog&#xff08;向下转型&#xff09; Dog放入到ArrayList 会先转成Object&#xff0c;在转…

YOLOv5-7.0改进(二)BiFPN替换Neck网络

前言 针对红绿灯轻量化检测&#xff0c;上一节使用MobileNetv3替换了主干网络&#xff0c;本篇将在使用BiFPN替换Neck的方式优化算法~ 往期回顾 YOLOv5-7.0改进&#xff08;一&#xff09;MobileNetv3替换主干网络 目录 一、BiFPN简介二、改进方法一第一步&#xff1a;在com…

[ES] ElasticSearch节点加入集群失败经历分析主节点选举、ES网络配置 [publish_address不是当前机器ip]

背景 三台CentOS 7.6.1虚拟机&#xff0c; 每台虚拟机上启动一个ElasticSearch 7.17.3&#xff08;下面简称ES&#xff09;实例 即每台虚拟机上一个ES进程&#xff08;每台虚拟机上一个ES节点&#xff09; 情况是&#xff1a; 之前集群是搭建成功的, 但是今天有一个节点一…

unity制作app(6)--服务器保存数据

1.成功将app的所有数据上传到客户端 2.把数据存储到txt文件中&#xff0c;照猫画虎修改create的内容&#xff0c;原内容如下&#xff1a; 写入函数调用的起始位置&#xff1a; 修改后的create内容如下&#xff1a; 3.最终写入文件的内容如下&#xff1a; 4.补充一下结构体的初…

现代制造之Cura切片

现代制造 有现代技术支撑的制造业&#xff0c;即无论是制造还是服务行业&#xff0c;添了现代两个字不过是因为有了现代科学技术的支撑&#xff0c;如发达的通信方式&#xff0c;不断发展的互联网&#xff0c;信息化程度加强了&#xff0c;因此可以为这两个行业增加了不少优势…

i春秋-Test

题目 解题 参考WP https://blog.csdn.net/qq_40654505/article/details/107142533/目录扫描 复现wp payload为&#xff1a; search.php?searchtype5&tid&areaeval($_POST[cmd])使用蚁剑连接 http://eci-2ze4iyhwj7xvb68bsb2t.cloudeci1.ichunqiu.com:80/search.ph…

语义分割——天空图像分割数据集

引言 亲爱的读者们&#xff0c;您是否在寻找某个特定的数据集&#xff0c;用于研究或项目实践&#xff1f;欢迎您在评论区留言&#xff0c;或者通过公众号私信告诉我&#xff0c;您想要的数据集的类型主题。小编会竭尽全力为您寻找&#xff0c;并在找到后第一时间与您分享。 …

差分约束 C++ 算法例题

差分约束 差分约束 是一种特殊的 n 元一次不等式组&#xff0c;m 个约束条件&#xff0c;可以组成形如下的格式&#xff1a; { x 1 − x 1 ′ ≤ y 1 x 2 − x 2 ′ ≤ y 2 ⋯ x m − x m ′ ≤ y m \begin{cases} x_1-x_1^{} \le y_1 \\ x_2-x_2^{} \le y_2 \\ \cdots \\ x_…

AR人像滤镜SDK解决方案,专业调色,打造个性化风格

视觉内容已成为企业传达品牌价值和吸引用户眼球的重要载体&#xff0c;为满足企业对于高质量、多样化视觉内容的迫切需求&#xff0c;美摄科技凭借先进的AR技术和深厚的图像处理经验&#xff0c;推出了业界领先的AR人像滤镜SDK解决方案。 一、一站式解决方案&#xff0c;覆盖多…

Emacs之取消sh-mode模式下:快捷键C-c C-c(一百三十七)

简介&#xff1a; CSDN博客专家&#xff0c;专注Android/Linux系统&#xff0c;分享多mic语音方案、音视频、编解码等技术&#xff0c;与大家一起成长&#xff01; 优质专栏&#xff1a;Audio工程师进阶系列【原创干货持续更新中……】&#x1f680; 优质专栏&#xff1a;多媒…

基于单片机的温度控制系统设计(51基础版)-设计说明书

本论文设计了一种基于51单片机的温度控制系统&#xff0c;该系统具备以下主要功能&#xff1a;首先&#xff0c;通过温度传感器实时检测环境温湿度&#xff0c;以获取准确的温度数值。其次&#xff0c;通过按键设置温度阈值&#xff0c;用户可以根据需求自行调整控制温度的上限…

You Only Cache Once:YOCO 基于Decoder-Decoder 的一个新的大语言模型架构

这是微软再5月刚刚发布的一篇论文提出了一种解码器-解码器架构YOCO&#xff0c;因为只缓存一次KV对&#xff0c;所以可以大量的节省内存。 以前的模型都是通过缓存先前计算的键/值向量&#xff0c;可以在当前生成步骤中重用它们。键值(KV)缓存避免了对每个词元再次编码的过程&…

WHAT - CSS Animationtion 动画系列(一)

目录 一、介绍二、animation-name三、animation-duration四、animation-timing-function4.1 介绍4.2 具体实践&#xff1a;抛物线 五、animation-delay六、animation-iteration-count七、animation-direction八、animation-fill-mode九、animation-play-state 今天我们主要学习…

HackMyVM-Minimal

目录 信息收集 arp nmap nikto whatweb WEB web信息收集 gobuster 文件包含漏洞 提权 web信息收集 main方法 question_1 question_2 question_3 prize.txt 软连接 信息收集 arp ┌──(root?0x00)-[~/HackMyVM] └─# arp-scan -l Interface: eth0, type: E…

中职智慧校园建设内容规划

1. 渠道先行 1) IT根底设施渠道是支撑智慧学校使用体系所必需的运转环境&#xff0c;是首要需求建造的内容&#xff0c;但是要遵从有用准则&#xff0c;IT设备开展很快&#xff0c;更新很快&#xff0c;不要片面追求全而新&#xff1b; 2) 使用根底渠道是支撑智慧学校使用体系作…

SCI一区论文蛇优化器(SO)独家原创改进!适合发表paper!

购买改进/原创算法避坑指南 这会触及很多人的利益&#xff0c;但是不得不发声&#xff0c;教大家避坑&#xff01;因为现在元启发式/群智能算法改进、原创算法市场太乱了&#xff0c;导致产生了很多受害者。 1、增加复杂度的不要买&#xff0c;大家可以叫商家给出运行时间比较…

Java 修饰符

Java 修饰符 Java语言提供了很多修饰符&#xff0c;主要分为以下两类&#xff1a; 访问修饰符 非访问修饰符 修饰符用来定义类、方法或者变量&#xff0c;通常放在语句的最前端。我们通过下面的例子来说明&#xff1a; public class ClassName { // … } private boolean myF…

74从零开始学Java之排序算法中的冒泡和选择排序

作者:孙玉昌,昵称【一一哥】,另外【壹壹哥】也是我哦 CSDN博客专家、万粉博主、阿里云专家博主、掘金优质作者 前言 我们要想成为一个优秀的程序员,其实非常关键的一点就是要锻炼培养自己的编程思维,就好比一个狙击手,要通过大量的射击训练要用大量的子弹喂出来。同样的…

第十三篇:智慧之网:深度探索关系型数据库的数学奥秘与实战技艺

智慧之网&#xff1a;深度探索关系型数据库的数学奥秘与实战技艺 1. 引言 1.1 数据时代的基石 在数字化的浪潮中&#xff0c;数据已成为新时代的石油&#xff0c;而关系型数据库则是这座数据矿藏的精炼厂。自E.F. Codd在1970年提出关系模型以来&#xff0c;关系型数据库以其坚…

新iPadPro是怎样成为苹果史上最薄产品的|Meta发布AI广告工具全家桶| “碾碎一切”,苹果新广告片引争议|生成式AI,苹果倾巢出动

Remini走红背后&#xff1a;AI生图会是第一个超级应用吗&#xff1f;新iPadPro是怎样成为苹果史上最薄产品的生成式AI&#xff0c;苹果倾巢出动Meta发布AI广告工具全家桶&#xff0c;图像文本一键生成解放打工人苹果新iPadPro出货量或达500万台&#xff0c;成中尺寸OLED发展关键…