手撕vector的模拟实现

𝙉𝙞𝙘𝙚!!👏🏻‧✧̣̥̇‧✦👏🏻‧✧̣̥̇‧✦ 👏🏻‧✧̣̥̇:Solitary_walk

      ⸝⋆   ━━━┓
     - 个性标签 - :来于“云”的“羽球人”。 Talk is cheap. Show me the code
┗━━━━━━━  ➴ ⷯ

本人座右铭 :   欲达高峰,必忍其痛;欲戴王冠,必承其重。

👑💎💎👑💎💎👑 
💎💎💎自💎💎💎
💎💎💎信💎💎💎
👑💎💎 💎💎👑    希望在看完我的此篇博客后可以对你有帮助哟

👑👑💎💎💎👑👑   此外,希望各位大佬们在看完后,可以互相支持,蟹蟹!
👑👑👑💎👑👑👑


目录:
一 构造函数
二 析构函数
三 size( )
四 capacity( )
五 reserve ()
六 resize ()
七 push_back()
八 insert()
九 erase()
十迭代器相关函数模拟

其实对于vector 模拟实现的学习和前面的string 是一样滴,学起来简直就是 so easy!

 首先需要我们对vector 这个类有一定的了解

其实vector 就是数据结构里面的顺序表

vector 里面有三个重要的指针,以下所有的模拟实现都是基于当前这三个指针进行滴,所以对着三个指针必须要深入了解

 结合草图进行理解:

 为了避免和库里面的vector 发生冲突,我把自己实现的vector 这个类放在一个命名空间里面

定义一个vector类:

1: 构造函数

 1)无参构造函数
vector()//无参构造函数:_start(nullptr),_finish(nullptr),_enofstorage(nullptr){}

 之所以选择对_start   _finish  _enofstorage 这三个指针进行初始化为空,就是避免在后面调用析构函数的时候导致程序崩溃

2)有参构造函数

1) 用 n 个 val 进行实例化

vector(size_t n, const T& val){reserve(n);for (size_t i = 0; i < n; i++)push_back(val);}

 2)迭代器区间进行初始化:注意是可以在一个类里面再写一个模板的

template <class InputIterator>// 在一个类模板里面是支持在写一个模板函数的vector(InputIterator first, InputIterator last){while (first != last){push_back(*first);++first;}}

 各位老铁看一下,下面问题是咋回事???

问题:

当对v2这个对象进行调用构造函数的时候,是可以编译通过的;但是当对v1这个对象进行构造函数调用的时候编译失败。

分析:

v2这个对象对应调用构造函数的参数的类型是 int ,string 类型,此时编译器会自动匹配最佳的函数,所以直接调用第2个构造函数;但是对于v1这个对象的调用构造函数的参数类型是 int ,int 类型此时编译器只会直接匹配第一个构造函数,之所以不匹配第2个构造函数是因为:要进行整型的提升,那编译器还不如直接走第一个构造函数的调用,但是此时的参数是int 类型不能进行解引用的所以编译器报错:非法的间接寻址

 

解决:

咱直接搞一个构造函数参数的类型是int  int  类型的不就OK了

 3)赋值运算符重载
	void swap(vector<T>& tmp){std::swap(_start, tmp._start);std::swap(_finish, tmp._finish);std::swap(_endofstoreage, tmp._endofstoreage);}vector<T>& operator=(vector<T>& tmp){swap(tmp);return *this;}
 4)[ ] 运算符重载
	T& operator[] (const size_t pos){assert(pos < size());//防止越界return _start[pos];}const T& operator[] (const size_t pos) const{assert(pos < size());//防止越界return _start[pos];}

2: 析构函数
~vector(){delete[] _start;_start = _finish = _enofstorage = nullptr;}
3:size( )
	size_t size(){return _finish - _start;//返回当前有效的数据个数}
4: capacity( )
	size_t capacity(){return _enofstorage - _start;//返回当前的空间容量}
5:reserve ()
reserve()坑点一:野指针

注意:这个是错误的代码

 分析:

 对当下问题的纠正:

reserve()坑点二:浅拷贝

借助对象v1 对v2进行实例化,以下程序会崩溃

 分析:

此时2个不同对象指向同一块空间,在调用析构函数的时候,v2这个对象先销毁(符合先创建的对象后析构),当v1调用析构函数的时候,此时所指向的空间已经归还系统,因此出现野指针,造成程序的崩溃

问题的本质:memcpy()这个函数在进行拷贝的时候是值拷贝(不管是内置类型还是自定义类型数据)

 解决:对数据进行深拷贝

//memcpy(tmp, _start, sizeof(T) * size());//err 浅拷贝for (size_t i = 0; i < sz; i++){tmp[i] = _start[i];}
正确代码:
	void reserve(size_t n){if (n > capacity()){size_t sz = size();//提前记录扩容之前的size()T* tmp = new T[n];// 数据拷贝if (_start){//memcpy(tmp, _start, sizeof(T) * size());//err 浅拷贝for (size_t i = 0; i < sz; i++){tmp[i] = _start[i];}delete _start;}//更新_start = tmp;_finish = _start + sz;_enofstorage = _start + n;}}
6:resize ()

 正确代码:

思考以下几个问题:

1) 为什么要调用构造函数进行指定的初始化 直接默认用0进行初始化不OK???     

2)可以不用引用吗

 1)之所以使用构造函数进行初始化是因为,并不确定当下T的类型是否为int,还是其他类型:char   vector<vector<int>>……

2)可以不使用引用第二个参数这样写也是OK的

const  T val =  T()

7: push_back()
void push_back(const T& v){// 是否扩容// _finish 进行++if (_finish == _enofstorage){size_t sz = size();//记录扩容之前的size() 避免后续野指针的出现size_t cp = capacity() == 0 ? 4 : 2 * capacity();T* tmp = new T[cp];delete[]_start;//旧空间释放if(_start)// 数据拷贝{memcpy(tmp, _start, sizeof(T) * size());}// 扩容之后的更新_start = tmp;_finish = _start + sz;_enofstorage = _start + cp;}//数据插入*(_finish) = v;++_finish;}
8:insert()

相信有很多老铁们会这样写代码吧(俺一开始就是这样搞滴)

错误代码:

 遇到问题不慌不忙,咱调试走起,瞧瞧咋回事

 分析:

 通过调试我们发现,在没有扩容之前程序是可以正常跑起来的,但是当涉及到扩容时,就出现了随机值在。

其实这就是我们所说的迭代器的内部失效的问题,要想再对迭代器更改后进行使用,我们可以记录一下pos 迭代器相对于起始位置的偏移量,下一次再使用的时候对pos 迭代器进行更新即可

 运行结果:

正确代码:

void insert(iterator  pos, const T& val){//默认pos 位置之前进行插入// pos 位置是否合法// 是否扩容assert(pos >= _start);assert(pos < _finish);if (_finish == _enofstorage){size_t sz = pos - _start;reserve(capacity() == 0 ? 4 : 2 * capacity());pos = sz + _start;//更新迭代器}iterator end = _finish;while (end > pos){*end = *(end - 1);--end;}*pos = val;++_finish;}
9: erase()

咱话不多说,代码先跑起来,康康!

可能有的老铁看到当前的运行结果觉得很满意:不错不错是我预期的(试过了各个位置的删除)

但是:也有些敏锐的老铁,会觉得有了上面insert()函数给我们的经验,erase() 函数也会涉及到迭代器实失效的问题

 通过以下程序的分析结果变可以看出:

 正确代码:
	void erase(iterator pos){assert(pos >= _start);assert(pos <= _finish);//数据覆盖vector<int>::iterator end = pos + 1;while (end < _finish){*(end - 1) = *end;++end;}--_finish;}while (pos != v1.end()){if (*pos % 2 == 0)v1.erase(pos);// 删除之后自然返回挪动数据之后的对应位置的迭代器,从而避免连续偶数的时候有没有删掉的else{++pos;}}

运行结果:

 通过对insert() erase() 调用我们发现:都会出现迭代器失效的问题

10:迭代器相关函数模拟
typedef T* iterator; // 默认受到类域的限制,所以默认属性是privatetypedef const T* const_iterator;iterator _start;  // 指向数据的起始位置iterator _finish; // 指向最后一个数据的下一个位置iterator _enofstorage;// 指向剩余容量的末尾位置//迭代器相关实现iterator begin(){return _start;}iterator end(){return _finish;}const_iterator begin()const{return _start;}const_iterator end()const{return _finish;;}
结语:以上就是要share 的内容,对于初次模拟实现vector的小白而言,还是多多少少有些坑注定少不了的,所以说咱也不要害怕踩坑。其实对这个vector模拟实现重点就是迭代器相关的使用以及理解,所以说咱还是多多敲敲代码,看看stl库里面相关的资料介绍,希望各位大佬们都有所收获!

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

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

相关文章

JDK14特性

JDK14 1 概述2 语法层面的变化1_instanceof的模式匹配(预览)2_switch表达式(标准)3_文本块改进(第二次预览)4_Records 记录类型(预览 JEP359) 3 API层面的变化4 关于GC1_G1的NUMA内存分配优化2_弃用SerialCMS,ParNewSerial Old3_删除CMS4_ZGC on macOS and Windows 4 其他变化1…

自学Redis7入门篇一

自学Redis7入门篇一 Redis概述一、Redis是什么&#xff1f;二、Redis能干什么&#xff1f;三、去哪里下四、Redis安装配置五、Redis 10数据类型1.字符串(String)2.列表(List)3.哈希表(Hash)4.集合(Set)5.有序集合(ZSet)6.地理空间(GEO)7.基数统计(HyperLogLog)8.位图(bitmap)9.…

计算机毕业设计PHP+vue体检预约管理系统d1yu38

防止在使用不同数据库时&#xff0c;由于底层数据库技术不同造成接口程序紊乱的问题。通过本次系统设计可以提高自己的编程能力&#xff0c;强化对所学知识的理解和运用 本系统是一个服务于医院先关内容的网站&#xff0c;在用户打开网站的第一眼就要明白网站开发的目的&#x…

01.本地工作目录、暂存区、本地仓库三者的工作关系

1.持续集成 1.持续集成CI 让产品可以快速迭代&#xff0c;同时还能保持高质量。 简化工作 2.持续交付 交付 3.持续部署 部署 4.持续集成实现的思路 gitjenkins 5.版本控制系统 1.版本控制系统概述2.Git基本概述3.Git基本命令 2.本地工作目录、暂存区、本地仓库三者的工作关系…

[数据结构]———归并排序

具体代码&#xff1a;在gitee仓库&#xff1a;登录 - Gitee.com 目录 ​编辑 1.基本思想&#xff1a; 2. 代码解析 1.分析 2.逻辑图 3.运行结果 1.基本思想&#xff1a; 归并排序&#xff08;MERGE-SORT&#xff09;是建立在归并操作上的一种有效的排序算法,该算法是采用分…

算法打卡day40

今日任务&#xff1a; 1&#xff09;139.单词拆分 2&#xff09;多重背包理论基础&#xff08;卡码网56携带矿石资源&#xff09; 3&#xff09;背包问题总结 4&#xff09;复习day15 139单词拆分 题目链接&#xff1a;139. 单词拆分 - 力扣&#xff08;LeetCode&#xff09; …

Pytorch学习笔记——TensorBoard的初使用

1、TensorBoard介绍 TensorBoard是TensorFlow的可视化工具&#xff0c;但它也可以与PyTorch结合使用。TensorBoard提供了一个Web界面&#xff0c;可以展示你训练过程中的各种信息&#xff0c;如损失值、准确度、权重分布等&#xff0c;更好地帮助开发者理解和调试模型。 Tenso…

huggingface里如何查看具体任务的评估指标

如果我们在做一个模型训练任务的时候&#xff0c;可能会不知道这个任务在评估的时候使用什么指标&#xff0c;那么huggingface里边为我们提供了参考&#xff1a; 下面就来看看吧&#xff1a; https://huggingface.co/https://huggingface.co/ 点击"Docs"&#xff…

【算法】【单调栈】【leetcode】1019. 链表中的下一个更大节点

刷这题之前先看&#xff1a; 【算法】【OD算法】【单调栈】找朋友-CSDN博客 【算法】【单调栈】【leetcode】1475. 商品折扣后的最终价格-CSDN博客 【算法】【单调栈】【leetcode】901. 股票价格跨度-CSDN博客 【算法】【单调栈】每日温度-CSDN博客 题目地址&#xff1…

嵌入式硬件中PCB走线与过孔的电流承载能力分析

简介 使用FR4敷铜板PCBA上各个器件之间的电气连接是通过其各层敷着的铜箔走线和过孔来实现的。 由于不同产品、不同模块电流大小不同,为实现各个功能,设计人员需要知道所设计的走线和过孔能否承载相应的电流,以实现产品的功能,防止过流时产品烧毁。 文中介绍设计和测试FR4敷…

iOS分享弹窗

klkxxy/分享菜单弹窗

抖音视频0粉营销推广墙纸,当日收益,第二天提现,日入300

项目简介&#xff1a; 这个项目非常易于执行&#xff0c;主要涉及在抖音平台上分享爱国主题的壁纸&#xff0c;并通过推广相关的小程序来实现盈利。 下 载 地 址 &#xff1a; laoa1.cn/1849.html 项目操作简便&#xff0c;一般只需花费1个小时即可完成&#xff0c;一旦掌…

408数据结构-二叉树的概念、性质与存储结构 自学知识点整理

前置知识&#xff1a;树的基本概念与性质 二叉树的定义 二叉树是一种特殊的树形结构&#xff0c;其特点是每个结点至多只有两棵子树&#xff08;即二叉树中不存在度大于 2 2 2的结点&#xff09;&#xff0c;并且二叉树是有序树&#xff0c;左右子树不能互换。 与树类似&#…

探索和构建 LLaMA 3 架构:深入探讨组件、编码和推理技术(三)KV缓存

探索和构建 LLaMA 3 架构&#xff1a;深入探讨组件、编码和推理技术&#xff08;三&#xff09; KV缓存 在推理的每一步中&#xff0c;只对模型输出的最后一个标记感兴趣&#xff0c;因为已经有了之前的标记。然而&#xff0c;模型需要访问所有先前的标记来决定输出哪个标记&…

07 - 步骤 javaScript代码

简介 JavaScript 代码是通过 JavaScript 脚本步骤来执行 JavaScript 脚本的一种方式。这允许用户在 Kettle 的数据流程中使用 JavaScript 编写自定义的脚本逻辑&#xff0c;用于数据处理、转换、计算等操作。 使用 场景 我需要在数据流加一个字段 createTime 当前时间&…

《有限元分析及应用》《有限元分析基础教程》-曾攀-清华大学|pdf电子书+有限元分析及应用视频教程(全85讲) 曾攀、雷丽萍 ​​​+课件PPT

专栏导读 作者简介&#xff1a;工学博士&#xff0c;高级工程师&#xff0c;专注于工业软件算法研究本文已收录于专栏&#xff1a;《有限元编程从入门到精通》本专栏旨在提供 1.以案例的形式讲解各类有限元问题的程序实现&#xff0c;并提供所有案例完整源码&#xff1b;2.单元…

找不到msvcr110.dll的多种解决方法,轻松解决dll问题

在日常使用计算机的时候&#xff0c;突然提示&#xff1a;“由于找不到msvcr110.dll&#xff0c;无法继续执行代码”。这个错误通常发生在运行某些程序时&#xff0c;系统无法找到所需的动态链接库文件。这个问题可能会给用户带来困扰&#xff0c;但是不用担心&#xff0c;下面…

QT:输入类控件的使用

LineEdit 录入个人信息 #include "widget.h" #include "ui_widget.h" #include <QDebug> #include <QString>Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget) {ui->setupUi(this);// 初始化输入框ui->lineEdit…

stm32f103zet6_串口实现-DHT11-tim1(定时)

1思路 1打开时钟 1.1使用定时器实现us级的计时 1.2在打开串口 1,3在DHT11驱动中修改引脚 stm32cudeMX 配置 1打开时钟 2打开串口 3打开tim1(定时器) 4生成代码 代码设置 1导入DHT11库(tim.h是定时器的文件系统自动生成的) DHT11.c #include "dht11.h" #inc…

真香!剪映专业版VIP,解锁限制功能!

01 软件介绍 剪映专业版采用更直观更全能易用的创作面板&#xff0c;让专业剪辑变得更简单高效&#xff0c;为更多人提供畅爽的专业剪辑体验&#xff0c;让更多人享受视频创作的乐趣! 剪映专业版引入强大黑罐头素材库&#xff0c;支持搜索海量音频、表情包、贴纸、花字、特效…