【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());//如果扩容了,要更新pospos = _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.mzph.cn/diannao/12750.shtml

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

相关文章

Promise 还能这样理解呀!

目录&#xff1a; 1、Promise是什么 2、Promise三种状态 3、Promise如何使用 4、Promise作用

一种快速提升文件传输速度的方法

在面对网络条件不理想时&#xff0c;进行文件传输往往会导致传输速率的显著下降。为了克服这一难题&#xff0c;镭速软件特别引入了一系列创新的设置选项&#xff0c;旨在显著提升文件传输速率。通过这些优化措施&#xff0c;用户即使在网络不佳的情况下&#xff0c;也能享受到…

机器人工具箱学习(三)

一、动力学方程 机器人的动力学公式描述如下&#xff1a; 式中&#xff0c; τ \boldsymbol{\tau} τ表示关节驱动力矩矢量&#xff1b; q , q ˙ , q \boldsymbol{q} ,\; \dot{\boldsymbol { q }} ,\; \ddot{\boldsymbol { q }} q,q˙​,q​分别为广义的关节位置、速度和加速…

uniapp如何打包预约上门按摩APP

uniapp如何打包预约上门按摩APP&#xff1f; 开发工具&#xff1a;HBuilderX 一、创建移动应用 1、 点击此处微信开放平台 2、点击【管理中心 - 移动应用 - 创建移动应用】填写资料后等待审核 app运行流程图 签名如何获取&#xff1a; 1&#xff09;先把打包好的app安装在手…

java多线程——线程池

概述 线程池是管理java线程生命周期的工具 降低资源消耗。通过池化技术能够重复利用已创建的线程&#xff0c;降低线程频繁创建和销毁造成的资源消耗提高线程的可管理性。无需程序员手动销毁线程&#xff0c;控制线程创建的数量&#xff0c;避免无限制的创建影响系统稳定性 …

OpenHarmony上移植memtester

1. 下载源码&#xff1a; wget https://pyropus.ca./software/memtester/old-versions/memtester-4.6.0.tar.gz 2. 解压并指定交叉编译方式 解压 tar -xvf memtester-4.6.0.tar.gz 修改conf-cc和conf-ld&#xff0c;指定交叉编译方式 conf-cc conf-ld 3. 编译 直接运行m…

Stable Diffusion入门使用技巧及个人实例分享--大模型及lora篇

大家好&#xff0c;近期使用Stable Diffusion比较多&#xff0c;积累整理了一些内容&#xff0c;得空分享给大家。如果你近期正好在关注AI绘画领域&#xff0c;可以看看哦。 本文比较适合已经解决了安装问题&#xff0c;&#xff08;没有安装的在文末领取&#xff09; 在寻找合…

分布式搜索——ElasticSeach简介

一般都用数据库存储数据&#xff0c;然后对数据库进行查询获取数据&#xff0c;但是当数据量很大时&#xff0c;查询效率就会很慢&#xff08;具体下面会讲到&#xff09;&#xff0c;所以这种情况下就会使用到ElasticSeach ElasticSeach的基本介绍 ElasticSeach是一 款非常强…

杰发科技AC7801——ADC之Bandgap和内部温度计算

0. 参考 电流模架构Bandgap设计与仿真 bandgap的理解&#xff08;内部带隙电压基准&#xff09; ​ ​ 虽然看不懂这些公式&#xff0c;但是比较重要的一句应该是这个&#xff1a;因为传统带隙基准的输出值为1.2V ​ 1. 使用 参考示例代码。 40002000是falsh控制器寄…

NXP RT1176(一)——二级BootLoader开发(安全引导加载程序SBL)

目录 1. 开发环境 2. 二级BOOT的功能 3. 步骤 3.1 配置源码 3.2 构建项目 3.2.1 MDK 3.2.2 IAR&#xff08;IAR也编译一下工程看看&#xff0c;这样两个平台都可以支持了&#xff09; 单核M7的开发&#xff01;&#xff01; 1. 开发环境 本文Windows下开发&#xff1a;…

MHD、MQA、GQA注意力机制详解

MHD、MQA、GQA注意力机制详解 注意力机制详解及代码前言&#xff1a;MHAMQAGQA 注意力机制详解及代码 前言&#xff1a; 自回归解码器推理是 Transformer 模型的 一个严重瓶颈&#xff0c;因为在每个解码步骤中加 载解码器权重以及所有注意键和值会产生 内存带宽开销 下图为三…

【合成孔径雷达】合成孔径雷达的多视角理解和时/频成像算法的统一解释

文章目录 一、什么是雷达成像&#xff08;1&#xff09;主要的遥感探测手段&#xff1a;光学、红外和雷达&#xff08;2&#xff09;从数学的角度&#xff1a;雷达成像主要研究什么&#xff1f;数据采集&#xff1a; y T x n yTxn yTxn信息提取&#xff1a; y − > x ? y…

编译错误:stray ‘\357’ in program的解决方法

目录 把报错文件更换编码格式&#xff0c;我试的utf-8 bom编码就可以了&#xff0c;可以多换几种试试。 网友的另一种案例&#xff1a; 编译错误&#xff1a;stray ‘\357’ in program的解决方法 把报错文件更换编码格式&#xff0c;我试的utf-8 bom编码就可以了&#xff0c…

如何同步管理1000个设备的VLAN数据?

什么是VLAN&#xff1f; VLAN&#xff0c;也就是虚拟局域网&#xff0c;是通过为子网提供数据链路连接来抽象出局域网的概念。在企业网中&#xff0c;一个企业级交换机一般是24口或者是48口&#xff0c;连接这些接口的终端在物理上形成一个广播域。广播域过大&#xff0c;就会导…

【AI智能体】零代码构建AI应用,全网都在喊话歌手谁能应战,一键AI制作歌手信息查询应用

欢迎来到《小5讲堂》 这是《文心智能体平台》系列文章&#xff0c;每篇文章将以博主理解的角度展开讲解。 温馨提示&#xff1a;博主能力有限&#xff0c;理解水平有限&#xff0c;若有不对之处望指正&#xff01; 目录 文心智能体大赛背景创建应用平台地址快速构建【基础配置】…

前端无样式id或者class等来定位标签

目录&#xff1a; 1、使用背景2、代码处理 1、使用背景 客户使用我们产品组件&#xff0c;发现替换文件&#xff0c;每次替换都会新增如下的样式&#xff0c;造就样式错乱&#xff0c;是组件的文件&#xff0c;目前临时处理的话就是替换文件时删除新增的样式&#xff0c;但是发…

【JVM】阅读Class字节码:常量池

目录 基本结构解析 常量池 常量池简介 如何阅读Class文件中的常量池信息 基本结构解析 Magic(魔数) Magic的唯一作用是确定这个文件是否为一个能被虚拟机所接受的class 文件。魔数值固定为0xCAFEBABE&#xff0c;不会改变。 常量池 常量池简介 下图是反编译过后的字节码文…

TensorFlow的学习

0.基础概念 术语表&#xff1a; https://developers.google.cn/machine-learning/glossary?hlzh-cn#logits 1.快速入门 https://tensorflow.google.cn/tutorials/quickstart/beginner?hlzh-cn 2.基于Keras进行图像分类 https://tensorflow.google.cn/tutorials/keras/cl…

gradle 共享存储挂载缓存目录的问题

2个任务同时构建的时候&#xff0c;报错如上。 原因&#xff1a;挂载目录的问题导致的&#xff0c;挂在最小粒度的目录下。 /home/app/.gradle/caches/modules-2/files-2.1 挂载到这个级别的目录下。

演员怎么上百度百科

百度百科是一个公正、开放、客观的平台&#xff0c;它为演员提供了一个展示自己过往经历和演艺生涯的平台。以下是百科优化网yajje总结的演员创建百度百科的一些步骤和注意事项&#xff1a; 创建演员百度百科的基本条件 人物影响力&#xff1a;演员创建百度百科需要满足官方的规…