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

news2024/11/19 12:31:37

目录

前言:

一:在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.coloradmin.cn/o/1594218.html

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

相关文章

【Proteus】51单片机对步进电机的控制

步进电机&#xff1a;将电脉冲信号转变为角位移或线位移的开换控制系统。在非超载的情况下&#xff0c;电机的转速、停止的位置只取决于脉冲信号的频率和脉冲数&#xff0c;而不受负载变化的影响&#xff0c;即给电机加一个脉冲信号&#xff0c;电机则转过一个步距角。 特点&am…

2024蓝桥A组A题

艺术与篮球&#xff08;蓝桥&#xff09; 问题描述格式输入格式输出评测用例规模与约定解析参考程序难度等级 问题描述 格式输入 无 格式输出 一个整数 评测用例规模与约定 无 解析 模拟就好从20000101-20240413每一天计算笔画数是否大于50然后天数&#xff1b; 记得判断平…

【STM32】西南交大嵌入式系统设计实验:环境配置

把走过的坑记录一下&#xff0c;希望后来人避坑 No ST-Link device detected.问题解决 如果跟着指导书出现这个问题&#xff1a; 直接跳过这一步不用再更新固件&#xff0c;后面直接创建项目写程序就行了。 在keil里配置成用DAP_link即可。 详细的可以看这篇文章&#xff1a…

如何进行宏观经济预测

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

实战项目——智慧社区(四)之 系统管理

1、用户管理 提供查询和搜索用户、根据id查询用户信息、添加用户、修改用户、删除用户的功能 界面 添加用户 修改用户信息 2、角色管理 提供查询和搜索角色、根据id查询角色信息、添加角色、修改角色、删除角色的功能 界面 添加角色 修改角色 3、菜单管理 提供查询和搜索菜…

JavaScript(七)-高级技巧篇

文章目录 深浅拷贝浅拷贝深拷贝 异常处理thorw抛异常try/catch捕获异常debugger 处理thisthis指向改变this 性能优化防抖lodash实现防抖手写防抖函数 节流 - throttle 深浅拷贝 浅拷贝 深拷贝 深拷贝有三种方式 通过递归实现深拷贝 一定先写数组再写对象 lodash/cloneDeep …

Gradle 在 Spring 中的使用-ApiHug准备-工具篇-006

&#x1f917; ApiHug {Postman|Swagger|Api...} 快↑ 准√ 省↓ GitHub - apihug/apihug.com: All abou the Apihug apihug.com: 有爱&#xff0c;有温度&#xff0c;有质量&#xff0c;有信任ApiHug - API design Copilot - IntelliJ IDEs Plugin | Marketplace ApiHug …

Linux(Ubuntu) 查看并删除使用【dpkg】安装的软件

目录 ■前言 ■查看安装的软件 ■删除安装的软件 正常删除&#xff08;dpkg -r xxxxName&#xff09; 问题解决&#xff1a;use --purge to remove them too ■其他调查信息 命令 图片1 图片2 图片3 ■前言 安装Mysql8.3失败 我的服务器-CSDN博客 ■查看安装的软…

Linux的学习之路:10、进程(2)

摘要 本章主要是说一下fork的一些用法、进程状态、优先级和环境变量。 目录 摘要 一、fork 1、fork的基本用法 2、分流 二、进程状态 三、优先级 四、环境变量 1、常见环境变量 2、和环境变量相关的命令 3、通过代码如何获取环境变量 五、导图 一、fork 1、fork…

`Object3D.lookAt()` 是 Three.js 中 `Object3D` 类的一个方法,用于让一个对象朝向指定的目标点。

demo案例 方法签名 object.lookAt(target)参数 target&#xff1a;目标点的坐标&#xff0c;可以是一个 Three.js 的 Vector3 对象&#xff0c;也可以是包含 x、y、z 坐标的普通 JavaScript 对象。 返回值 该方法没有返回值。 功能 该方法将当前对象的 z 轴指向目标点&am…

GitHub repository - Pulse - Contributors - Network

GitHub repository - Pulse - Contributors - Network 1. Pulse2. Contributors3. NetworkReferences 1. Pulse 显示该仓库最近的活动信息。该仓库中的软件是无人问津&#xff0c;还是在火热地开发之中&#xff0c;从这里可以一目了然。 2. Contributors 显示对该仓库进行过…

【Golang学习笔记】从零开始搭建一个Web框架(四)

文章目录 模板渲染静态文件支持HTML 模板渲染 错误恢复完结撒花~~ 前情提示&#xff1a; 【Golang学习笔记】从零开始搭建一个Web框架&#xff08;一&#xff09;-CSDN博客 【Golang学习笔记】从零开始搭建一个Web框架&#xff08;二&#xff09;-CSDN博客 【Golang学习笔记】从…

【C++成长记】C++入门 | 类和对象(上) |类的作用域、类的实例化、类的对象大小的计算、类成员函数的this指针

&#x1f40c;博主主页&#xff1a;&#x1f40c;​倔强的大蜗牛&#x1f40c;​ &#x1f4da;专栏分类&#xff1a;C❤️感谢大家点赞&#x1f44d;收藏⭐评论✍️ 目录 一、类的作用域 二、类的实例化 三、类对象模型 四、this指针 1、this指针的引出 2 this指针的特…

Linux学习笔记之9(消息队列)

Linux learning 1、引言2、创建一个消息队列3、发送和接受消息3.1、发送消息3.1、接收消息 4、删除一个消息队列5、例程 1、引言 消息队列&#xff08;message queue&#xff09;也是进程之间通信的一种方式&#xff0c;相比于共享内存的通信方式&#xff0c;消息队列也有类型…

如何更好地理解 Vue 3 watch 侦听器的用法

&#x1f90d; 前端开发工程师、技术日更博主、已过CET6 &#x1f368; 阿珊和她的猫_CSDN博客专家、23年度博客之星前端领域TOP1 &#x1f560; 牛客高级专题作者、打造专栏《前端面试必备》 、《2024面试高频手撕题》 &#x1f35a; 蓝桥云课签约作者、上架课程《Vue.js 和 E…

电脑录屏软件哪个好用又免费?市面20款录屏软件测评结果

随着在线教学、远程办公和自媒体创作的兴起&#xff0c;电脑录屏软件逐渐成为了许多用户的必备工具。市面上的录屏软件琳琅满目&#xff0c;但真正既好用又免费的却并不多见。为了帮助大家找到心仪的录屏软件&#xff0c;我们对市面上20款热门免费录屏软件进行了详细的测评。 电…

【游戏开发之热更新技术】

游戏开发之热更新技术 热更新技术是指在不重新发布和安装应用的情况下&#xff0c;对已部署的应用程序进行更新和修补的技术。这种技术在现代软件开发中变得越来越重要&#xff0c;因为它能够为用户提供更加及时的服务和更好的体验。以下是一篇关于热更新技术的文章&#xff0…

浦大喜奔APP8.0智能升级,发力数字金融深化五大金融篇章服务

1. 浦大喜奔立足科技赋能持续迭代升级&#xff0c;筑牢用户体验护城河 浦发信用卡中心坚持数字科技与客户体验双轮驱动&#xff0c;以科技赋能发展&#xff0c;优化整体系统性能&#xff0c;全方位支撑浦大喜奔 APP提高线上客户服务能力与体验&#xff0c;积极服务民生消费&a…

C++的引用和内联函数,auto

什么是引用 引用就是取别名 可以给一个变量取多个别名,也可以给别名取别名 别名与本名拥有同一地址,对任意别名修改,也会同时修改其他别名和本名 引用的作用 引用的作用与指针重叠度很高 图中函数的参数int& a,int& b,a是x的别名,b是y的别名 则ab的交换就是xy的交…

Python 编程 深入了解内存管理机制、深拷贝与浅拷贝

&#x1f349; CSDN 叶庭云&#xff1a;https://yetingyun.blog.csdn.net/ 一、对象和引用、内存管理机制 Python 中的一切都是对象&#xff0c;包括数字、字符串、列表和函数等。为了简化内存管理并提高效率&#xff0c;Python 采用了统一的对象模型。在这个模型中&#xff0c…