c++修炼之路之vector模拟实现

目录

前言:

一:在STL的开源代码中的vector的实现

二:模拟实现

1.数据成员+size()+capacity()

2.resize+reserve

 3.构造函数+析构函数+赋值重载

4.迭代器+[] 

5.push_back+insert+erase+迭代器失效问题 

三:测试用例和全部代码

接下来的日子会顺顺利利,万事胜意,生活明朗-----------林辞忧  

前言:

在了解标准库中的vector之后,对于模拟实现vecor的各接口函数常常是面试时的重难点,在模拟实现vector的过程中主要有迭代器失效的问题,在模拟实现的vectro中是使用三指针来模拟实现vector的,接下来我们就开始介绍vector的模拟实现

一:在STL的开源代码中的vector的实现

对于vector的模拟实现和string的完全不同,vector采用的是三指针来模拟实现

使用的是start,finish,endofstorage三个指针 ,在看源代码时如果不懂这三个指针的指向位置的话,则可以先看size和capacity的实现来猜测,则

finish-start=size 有效数据的大小

endofstorage-start=capacity 容量

二:模拟实现

1.数据成员+size()+capacity()

typedef T*  iterator;private:iterator _start=nullptr;iterator _finish=nullptr;iterator _endofstorage=nullptr;size_t size()const
{return _finish - _start;
}
size_t capacity()const
{return _endofstorage - _start;
}

2.resize+reserve

对于在reserve的操作中会出现一些问题,首先是如果T是自定义类型的话,不能使用浅拷贝,得使用深拷贝,在操作过程中注意_finish的大小,如果不注意的话,_finish是不改变的,导致没有扩容,访问数据的话会直接越界访问报错

void reserve(size_t n)//开空间
{if (n > capacity()){T* tmp = new T[n];//开空间size_t sz = size();if (_start){//问题1:// memcpy是按字节来拷贝的,完成的是浅拷贝,如果T是string的话//浅拷贝会直接出错的,应该是实现为深拷贝//memcpy(tmp, _start, sizeof(T) * size());//拷贝数据for (int i = 0; i < sz; i++){tmp[i] = _start[i];}delete[]_start;//释放旧空间}//改变指向_start = tmp;//_finish = _start + size();//问题2:这样写的话,size()=_finish-_start   =>  _finish=_finish;// 还是原先的_finish没有改变(扩容)在访问数据时就会出错_finish = _start + sz;_endofstorage = _start + n;}
}
void resize(size_t n, const T& val = T())//匿名对象具有常行,加const修饰
{if (n <= size())//删除数据{_finish = _start + n;}else{reserve(n);while (_finish<_start+n){*_finish = val;++_finish;}}
}

 3.构造函数+析构函数+赋值重载

注意尤其是拷贝构造,如果T是自定义类型的话,我们不写编译器自动生成的完成的是浅拷贝,浅拷贝就会发生问题的,所以得自己实现深拷贝

vector()//无参构造
{}//使用一段迭代器区间来构造
template<class InputIterator>
vector(InputIterator first, InputIterator last)
{while (first != last){push_back(*first);++first;}
}vector(size_t n, const T&val=T())//n个val构造   //缺省值给匿名对象
{reserve(n);//开空间for (int i = 0; i < n; i++){push_back(val);}
}//处理vector<int> v1(10,0)这种的,如果不重载的话,会自动匹配到迭代器区间构造
vector(int n, const T& val = T())//n个val构造   //缺省值给匿名对象
{reserve(n);//开空间for (int i = 0; i < n; i++){push_back(val);}
}//拷贝构造 vector<int> v2(v1)
vector(const vector<T>& v)
{reserve(v.capacity());//开空间//最好进行深拷贝for (auto& c : v){push_back(c);}
}void swap(vector<T>& v)
{std::swap(_start, v._start);std::swap(_finish, v._finish);std::swap(_endofstorage, v._endofstorage);
}
//赋值重载 v1=v2;
vector<int>& operator=(vector<int> v)
{swap(v);return *this;
}~vector()
{delete[] _start;_start = _finish = _endofstorage = nullptr;
}

4.迭代器+[] 

迭代器有着普通迭代器和const成员使用的const迭代器,有了迭代器就支持范围for来遍历数据

对于下标+[]一样,也有const对象遍历时使用的

typedef T*  iterator;
typedef const T* const_iterator;iterator begin()
{return _start;
}
iterator end()
{return _finish;
}const_iterator begin()const
{return _start;
}
const_iterator end()const
{return _finish;
}
T &operator[](size_t pos)
{assert(pos < size());return *(_start + pos);
}const T& operator[](size_t pos)const
{assert(pos < size());return *(_start + pos);
}

5.push_back+insert+erase+迭代器失效问题 

迭代器失效就是迭代器底层对应指针指向的空间被销毁了,而使用一块已经被释放的空间,程序会崩溃,引起迭代器失效的这些操作主要是与扩容有关的操作

对于insert插入数据要扩容时,由于之前的pos在扩容之前的数组中。在扩容后原数组被释放,pos还在原数组中,就会变为野指针,在扩容后的数组中没有pos指针,在insert操作时就报错,解决办法就是重新赋值

对于erase也是如此会有迭代器失效的问题, 

 

这里使用的是传值传参,形参的改变不影响实参,底层空间是不改变的,如果没有返回 值的话,pos就会失效的,下次使用vs就会直接报错的,解决办法就是传值返回

 

void push_back(const T& val)
{if (_finish == _endofstorage){reserve(capacity() == 0 ? 4 : capacity() * 2);}*_finish = val;++_finish;//insert(end(), val);复用
}
void insert(iterator pos,const T& val)//在pos位置插入val
{assert(pos >= _start);assert(pos <= _finish);if (_finish == _endofstorage){//问题3:需要注意在开空间后pos位置还在原数组,此时delete原数组后,pos变成野指针//解决办法:先保存下来与_start的距离,在开空间之后在_start+距离即是pos的位置size_t len = pos - _start;reserve(capacity() == 0 ? 4 : capacity() * 2);pos = _start + len;}//挪动数据iterator end = _finish;while (end > pos){*end = *(end - 1);--end;}*pos = val;++_finish;
}
iterator erase(iterator pos)
{assert(pos >= _start);assert(pos < _finish);// vs2019进行强制检查,erase以后认为it失效了,不能访问,访问就报错//解决办法:使用返回值iterator end = pos + 1;while (end < _finish){*(end - 1) = *end;++end;}--_finish;return pos;
}

三:测试用例和全部代码

https://gitee.com/lin-ciyu/cplusplus/tree/master/my_vector/my_vector

对于vector的模拟实现还是有好多细节的,由于是迭代器失效问题和深浅拷贝问题等都须注意

 

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

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

相关文章

【LAMMPS学习】八、基础知识(1.8)键的断裂

8. 基础知识 此部分描述了如何使用 LAMMPS 为用户和开发人员执行各种任务。术语表页面还列出了 MD 术语&#xff0c;以及相应 LAMMPS 手册页的链接。 LAMMPS 源代码分发的 examples 目录中包含的示例输入脚本以及示例脚本页面上突出显示的示例输入脚本还展示了如何设置和运行各…

【C++庖丁解牛】哈希表/散列表的设计原理 | 哈希函数

&#x1f341;你好&#xff0c;我是 RO-BERRY &#x1f4d7; 致力于C、C、数据结构、TCP/IP、数据库等等一系列知识 &#x1f384;感谢你的陪伴与支持 &#xff0c;故事既有了开头&#xff0c;就要画上一个完美的句号&#xff0c;让我们一起加油 目录 前言1.哈希概念2.哈希冲突…

推荐七个Python效率工具!

为了提高效率&#xff0c;我们在平时工作中常会用到一些Python的效率工具&#xff0c;Python作为比较老的编程语言&#xff0c;它可以实现日常工作的各种自动化。为了更便利的开发项目&#xff0c;这里给大家推荐几个Python的效率工具。 1、Pandas-用于数据分析 Pandas是一个强…

复杂DP算法(动态规划)

复杂DP算法 一、线性DP例题1、鸣人的影分身题目信息思路题解 2、糖果题目信息思路题解 二、区间DP例题密码脱落题目信息思路题解 三、树状DP例题生命之树题目信息思路题解 一、线性DP 例题 1、鸣人的影分身 题目信息 思路 题解 #include <bits/stdc.h> #define endl …

ZISUOJ 数据结构-线性表

题目列表&#xff1a; 问题 A: 逆序链表建立 思路&#xff1a; 可以使用头插法插入所有元素后正序遍历输出或者使用尾插法逆序遍历&#xff0c;推荐使用双链表。这是链表系列的第一个题&#xff0c;那这个题下面的参考题解的各种解法我会尽可能写全一些。 参考题解1&#xff0…

【OTA】STM32-OTA升级——持续更新

【OTA】STM32-OTA升级——持续更新 文章目录 前言一、ymodem串口协议1、Ymodem 协议2、PC3、蓝牙4、WIFI云平台 二、UDS车载协议1.UDS协议 总结 前言 提示&#xff1a;以下是本篇文章正文内容&#xff0c;下面案例可供参考 一、ymodem串口协议 1、Ymodem 协议 STM32 Ymodem …

【I/O】基于事件驱动的 I/O 模型---Reactor

Reactor 模型 BIO 到 I/O 多路复用 为每个连接都创建一个线程 假设我们现在有一个服务器&#xff0c;想要对接多个客户端&#xff0c;那么最简单的方法就是服务端为每个连接都创建一个线程&#xff0c;处理完业务逻辑后&#xff0c;随着连接关闭线程也要销毁&#xff0c;但是…

每日一题(leetcode238):除自身以外数组的乘积--前缀和

不进阶是创建两个数组&#xff1a; class Solution { public:vector<int> productExceptSelf(vector<int>& nums) {int nnums.size();vector<int> left(n);vector<int> right(n);int mul1;for(int i0;i<n;i){mul*nums[i];left[i]mul;}mul1;for…

前端开发攻略---根据音频节奏实时绘制不断变化的波形图。深入剖析如何通过代码实现音频数据的可视化。

1、演示 2、代码分析 逐行解析 JavaScript 代码块&#xff1a; const audioEle document.querySelector(audio) const cvs document.querySelector(canvas) const ctx cvs.getContext(2d)这几行代码首先获取了 <audio> 和 <canvas> 元素的引用&#xff0c;并使用…

Java编程练习之抽象类与抽象方法

使用抽象类和抽象方法时&#xff0c;需要遵循以下原则&#xff1a; 1&#xff09;在抽象类中&#xff0c;可以包含抽象方法&#xff0c;也可以不包含抽象方法&#xff0c;但是包含了抽象方法的类必须被定义为抽象类&#xff1b; 2&#xff09;抽象类不能直接被实例化&#xf…

BugKu:Flask_FileUpload

Flask_FileUpload 解题思路 查看源码&#xff1a; 编写上传的文件 获得结果 小结 文件上传漏洞&#xff1a; 文件上传漏洞是指用户上传了一个可执行的脚本文件&#xff0c;并通过此脚本文件获得了执行服务器端命令的能力。这种攻击方式是最为直接和有效的&#xff0c;“文件上…

探索ERC20代币:构建您的第一个去中心化应用

下面文章中会涉及到该资源中的代码&#xff0c;如果想要完整版代码可以私信我获取&#x1f339; 文章目录 概要整体架构流程技术名词解释ERC20智能合约web3.js 技术细节ERC20合约部署创建前端界面前端与智能合约互连运行DAPP 小结 概要 在加密货币世界中&#xff0c;ERC20代币…

poi-tl的使用(通俗易懂,全面,内含动态表格实现 包会!!)

最近在做项目时候有一个关于解析Html文件&#xff0c;然后将解析的数据转化成word的需求&#xff0c;经过调研&#xff0c;使用poi-tl来实现这个需求&#xff0c;自己学习花费了一些时间&#xff0c;现在将这期间的经验总结起来&#xff0c;让大家可以快速入门 poi-tl的介绍 …

大数据产品有哪些分类?各类里知名大数据产品都有哪些?

随着互联网技术的持续进步和全球数字化转型的推进&#xff0c;我们正处于一个数据爆炸的时代。在这样的大背景下&#xff0c;大数据已经逐渐崭露头角&#xff0c;成为了推动各行各业发展的关键因素和核心资源。大数据不仅仅是指数据的规模巨大&#xff0c;更重要的是它蕴含的价…

Python八股文:基础知识Part2

1. Python中变量的保存和访问 Python中的变量实际上是一个指向对象的引用&#xff0c;每个对象都有一个唯一的标识符&#xff08;即内存地址&#xff09;。对于一些不可变对象&#xff0c;如字符串和整数&#xff0c;因为它们的值不可更改&#xff0c;所以当多个变量引用相同的…

彩虹聚合DNS管理系统源码

聚合DNS管理系统可以实现在一个网站内管理多个平台的域名解析&#xff0c;目前已支持的域名平台有&#xff1a;阿里云、腾讯云、华为云、西部数码、CloudFlare。本系统支持多用户&#xff0c;每个用户可分配不同的域名解析权限&#xff1b;支持API接口&#xff0c;支持获取域名…

建造者模式:构造复杂对象的艺术

在面向对象的设计中&#xff0c;建造者模式是一种重要的创建型设计模式&#xff0c;专门用来构建复杂的对象。它主要目的是将对象的构造代码与其表示代码分离&#xff0c;使同样的构建过程可以创建不同的表示。本文将详细介绍建造者模式的定义、实现、应用场景以及优缺点&#…

虚拟货币:数字金融时代的新工具

在数字化时代的到来之后&#xff0c;虚拟货币逐渐成为了一种广为人知的金融工具。虚拟货币是一种数字化的资产&#xff0c;它不像传统货币那样由政府或中央银行发行和监管。相反&#xff0c;虚拟货币通过密码学技术和分布式账本技术来实现去中心化的发行和交易。 虚拟货币的代…

内网通如何去除广告,内网通免广告生成器

公司使用内网通内部传输确实方便&#xff01;但是会有广告弹窗推送&#xff01;这个很烦恼&#xff01;那么如何去除广告呢&#xff01; 下载&#xff1a; 链接&#xff1a;https://pan.baidu.com/s/1CVVdWexliF3tBaFgN1W9aw?pwdhk7m 提取码&#xff1a;hk7m ID&#xff1a;…

如何进行宏观经济预测

理性预期经济学提出了理性预期的概念&#xff0c;强调政府在制定各种宏观经济政策时&#xff0c;要考虑到各行为主体预期对政策实施有效性的影响&#xff0c;积极促成公众理性预期的形成&#xff0c;从而更好地实现宏观调控的目标。政府统计要深入开展统计分析预测研究&#xf…