手撕vector的模拟实现

news2024/12/28 3:34:53

𝙉𝙞𝙘𝙚!!👏🏻‧✧̣̥̇‧✦👏🏻‧✧̣̥̇‧✦ 👏🏻‧✧̣̥̇: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; // 默认受到类域的限制,所以默认属性是private
		typedef 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.coloradmin.cn/o/1640728.html

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

相关文章

Python梯度提升决策树库之lightgbm使用详解

概要 LightGBM是一个快速、分布式、高性能的梯度提升决策树(Gradient Boosting Decision Tree)库,它在机器学习和数据挖掘领域被广泛应用。本文将介绍LightGBM库的安装方法、主要特性、基本功能、高级功能、以及在实际应用中的场景和总结。 安装 首先,需要安装LightGBM库…

【doghead】ubuntu构建libuv

按照官方的文档2024年3月的版本。首先构建libuv 最终构建的还得了test 构建过程 zhangbin@DESKTOP-1723CM1:/mnt/d/XTRANS/thunderbolt/ayame/zhb-bifrost$ ls Bifrost-202403 README.md draw player-only worker 大神的带宽估计.png zhangbin@DESKTOP-1723CM1:/mnt/d/XTRANS/…

python学习笔记B-20:序列实战--处理千年虫

将2位数表达的年份&#xff0c;转换为用4位数表达&#xff1a; print("将列表中的2位数年份转换为4位数年份") lst[88,89,90,00,99] print(lst) for index in range(len(lst)):if len(str(lst[index]))2:lst[index] 1900int(lst[index])elif len(str(lst[index]))1…

AI学习指南-人工智能概述

欢迎来到人工智能的奇妙世界&#xff01;如果你是初学者&#xff0c;那么你来对地方了。今天&#xff0c;我们将一起探索人工智能&#xff08;AI&#xff09;的基本概念&#xff0c;看看它是如何分类的&#xff0c;它的应用有哪些&#xff0c;以及未来可能的发展方向。准备好了…

7个策略,让你的可视化大屏打动人心!

要打动人心的可视化大屏&#xff0c;可以采取以下策略&#xff1a; 引人入目的设计&#xff1a; 选择鲜明而吸引人的颜色和视觉效果&#xff0c;使用引人注目的动画和过渡效果&#xff0c;以及吸引眼球的图形和图案设计。通过精心设计的布局和排版&#xff0c;确保信息清晰可…

vivado Aurora 8B/10B IP核(9)- CRC、 Aurora 8B/10B内核的时钟接口端口

CRC 模块提供 16 位或 32 位 CRC&#xff0c;用于用户数据。 Aurora 8B/10B 内核的时钟接口端口 从相邻收发器四边形的时钟Xilinx 实现工具可以根据需要对南北路由和引脚交换到收发器时钟输入进行必要的调整&#xff0c;以将时钟从一个四线到另一个。 重要信息&#xff1a;共…

HTML5实用大全(Part.2)

引言&#xff1a; 哈喽&#xff0c;各位小伙伴们大家好呀&#xff0c;学习了上一篇关于HTML5的文章后&#xff0c;你是否对于入门HTML5有了一定的基础了呢&#xff0c;本篇博客我们将继续学习HTML5的不同标签&#xff0c;跟上队伍&#xff0c;准备出发咯&#xff01; 1.标签之…

RMQ从入门到精通

一.概述与安装 //RabbitMQ //1.核心部分-高级部分-集群部分 //2.什么是MQ 消息队列message queue 先入先出原则;消息通信服务 //3.MQ的大三功能 流量消峰 应用解耦 消息中间件 //&#xff08;1&#xff09;人-订单系统(1万次/S)—> 人 - MQ(流量消峰,对访问人员进行排队) -…

Qt_介绍_环境安装_创建新项目_实现helloworld_坐标_1

文章目录 一、Qt是什么二、Qt的发展史三、Qt支持的平台四、Qt版本五、Qt的优点六、Qt的应用场景七、Qt开发环境&#xff0c;需要按照3个部分1.C编译器&#xff08;gcc,cl.exe....不是Visual Studio&#xff09;2.Qt SDK3.需要有一个Qt的集成开发环境&#xff08;IDE&#xff09…

云服务器+ASF实现全天挂卡挂时长

目录 前言正文1.安装下载2.编辑配置文件3.设置Steam社区证书4.启动ASF5.给游戏挂时长6.进阶-ASF自动启动且后台保活 前言 我遇到的最大的问题是&#xff0c;网络问题 其实不然&#xff0c;各大厂商的云服务器后台都有流量监控&#xff0c;意味着依靠一般方法是不能正常访问St…

最新可商用级chatgpt4.0系统源码+Midjourney-AI绘画系统 集成国内外数十个AI大模型和绘画接口

随着科技的飞速发展&#xff0c;人工智能正在逐步改变我们的生活&#xff0c;特别是在艺术创作领域。分享一款最新可商用级ChatGPT 4.0系统源码与Midjourney AI绘画系统的完美结合&#xff0c;集成国内外数十个AI大模型与绘画接口&#xff0c;为创作者们带来了前所未有的智能创…

DS:链表的分类

欢迎来到Harper.Lee的学习世界&#xff01; 博主主页传送门&#xff1a;Harper.Lee的博客主页 想要一起进步的uu欢迎来后台找我哦&#xff01; 链表的结构⾮常多样&#xff0c;以下情况组合起来就有8种&#xff08;2 * 2 * 2&#xff09;链表结构。下面我们依次来认识它们吧&am…

centos7安装真的Redmine-5.1.2+ruby-3.0.0

下载redmine-5.1.2.tar.gz&#xff0c;上传到/usr/local/目录下 cd /usr/local/ tar -zxf redmine-5.1.2.tar.gz cd redmine-5.1.2 cp config/database.yml.example config/database.yml 配置数据连接 #编辑配置文件 vi config/database.yml #修改后的内容如下 product…

如何基于nginx组建多个子目录网站

华子目录 实验要求实验步骤 实验要求 组建多个子目录网站www.openlab.com&#xff0c;该网站有2个子目录www.openlab.com/sxhkt和www.openlab.com/zywww.openlab.com/sxhkt使用http读取www.openlab.com/zy使用https读取 实验步骤 准备工作 [rootserver ~]# setenforce 0[ro…

深度学习之基于YOLOv5的山羊行为识别系统

欢迎大家点赞、收藏、关注、评论啦 &#xff0c;由于篇幅有限&#xff0c;只展示了部分核心代码。 文章目录 一项目简介 二、功能三、系统四. 总结 一项目简介 深度学习之基于YOLOv5的山羊行为识别系统是一个创新的项目&#xff0c;旨在通过深度学习和目标检测技术&#xff0c…

Nginx三大功能详解

文章目录 Nginx&#xff08;动静分离&#xff09;1.基本介绍2.需求分析3.思路分析4.先使用传统的方式1.配置win的tomcat1.webapps\search\cal.jsp2.webapps\search\image\cal.jpg3.启动tomcat&#xff0c;访问 http://192.168.200.1:8080/search/cal.jsp 2.配置linux的tomcat1.…

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

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

通义灵码:智能编码的革命性助手

通义灵码是由阿里云推出的一款基于通义大模型的智能编码辅助工具&#xff0c;它通过先进的人工智能技术&#xff0c;为开发者提供了一系列的智能编码功能&#xff0c;极大地提升了编码效率和质量。以下是通义灵码的一些核心功能和应用案例。 核心功能 代码智能生成 通义灵码…

轻量级分布式任务调度平台:XXL-JOB

目录 1 介绍1.1 特性1.2 整体架构 2 快速导入2.1 测试工程导入2.1 初始化数据库2.3 Docker安装任务管理中心 3 XXL-JOB任务注册测试3.1 引入xxl-job依赖3.2 配置xxljob相关信息3.3 定义定时任务执行方法3.3 配置任务执行器 4 CRON表达式4.1 cron表达式语法介绍4.2 cron练习 1 介…

Redis 源码学习记录:字符串

redisObject Redis 中的数据对象 server/redisObject.h 是 Redis 对内部存储的数据定义的抽象类型其定义如下&#xff1a; typedef struct redisObject {unsigned type:4; // 数据类型&#xff0c;字符串&#xff0c;哈希表&#xff0c;列表等等unsigned encoding:4; …