c++:vector模拟实现

news2024/12/23 6:27:55

一、vector成员变量

 库里实现用的就是这三个成员变量,咱们实现跟库里一样,

namespace myvector {
	template<class T>
	class vector
	{
	public:
		//vecttor的迭代器是原生指针
		typedef T* iterator;
		typedef const T* const_iterator;	
    private:
		iterator _start=nullptr;//数据块的开始--------------_begin
		iterator _finish=nullptr;//有效数据的尾-------------_end
		iterator _end_of_storage=nullptr;//存储容量的尾-----_capacity
    }

 这里把T*重命名为iterator,因为vector的迭代器就是原生指针,所以直接用就行。

二、默认成员函数

1.构造函数

        //我们在成员变量声明处给了缺省值,这里就可以不用初始化了
        vector()
		{}
		//T()是匿名对象,初始化n个value
		vector(size_t n, const T& value = T())
		{
			resize(n, value);
		}
		/*
		* 理论上讲,提供了vector(size_t n, const T& value = T())之后
		* vector(int n, const T& value = T())就不需要提供了,但是对于:
		* vector<int> v(10, 5);
		* 编译器在编译时,认为T已经被实例化为int,而10和5编译器会默认其为int类型
		* 就不会走vector(size_t n, const T& value = T())这个构造方法,
		* 最终选择的是:vector(InputIterator first, InputIterator last)
		* 因为编译器觉得区间构造两个参数类型一致,因此编译器就会将InputIterator实例化为int
		* 但是10和5根本不是一个区间,编译时就报错了
		* 故需要增加该构造方法
		*/
        //初始化 n个value
		vector(int n, const T& value = T())
		{
			resize(n, value);
		}
	    // 若使用iterator做迭代器,会导致初始化的迭代器区间[first,last)只能是vector的迭代器
		// 重新声明迭代器,迭代器区间[first,last)可以是任意容器的迭代器
        //初始化一段空间
		template<class InputIterator>
		vector(InputIterator first, InputIterator last)
		{
			while (first != last)
			{
				push_back(*first);
				++first;
			}
		}

2.拷贝构造和赋值运算符重载

		//拷贝构造
		vector(const vector<T>&v)
		{
			_start = new T[v.capacity()];
			//vector是深拷贝,但是vector空间上存一个开空间的自定义类型数组,例:string的数组
			//使用memcpy导致string对象的浅拷贝
			//memcpy(tmp, _start, sizeof(T) * size());
			for (size_t i = 0; i < v.size(); i++)
			{
				//假设T是string类型,这里调用string的赋值运算符重载,是深拷贝
				_start[i]=v._start[i];
			}
			_finish = v.size() + _start;
			_end_of_storage = v.capacity() + _start;
		}
		vector<T>& operator=(vector<T> v)
		{
			swap(v);
			return *this;
		}
        void swap(vector<T>& v)
		{
			std::swap(v._start, _start);
			std::swap(v._finish, _finish);
			std::swap(v._end_of_storage, _end_of_storage);
		}

这里赋值运算符重载的形参是传值, 会调用拷贝构造出一个临时对象v,(深拷贝),然后用库里的swap把v的参数交换,交换完后待赋值运算符重载结束后会自动析构掉临时对象v。

  1. memcpy是内存的二进制格式拷贝,将一段内存空间中内容原封不动的拷贝到另外一段内存空间中
  2.  如果拷贝的是自定义类型的元素,memcpy既高效又不会出错,但如果拷贝的是自定义类型元素,并且自定义类型元素中涉及到资源管理时,就会出错,因为memcpy的拷贝实际是浅拷贝。

 

 结论:如果对象中涉及到资源管理时,千万不能使用memcpy进行对象之间的拷贝,因为memcpy是浅拷贝,否则可能会引起内存泄漏甚至程序崩溃。

3.迭代器

        //vecttor的迭代器是原生指针
		typedef T* iterator;
		typedef const T* const_iterator;
		iterator begin()
		{
			return _start;
		}
		iterator end()
		{
			return _finish;
		}
		const_iterator cbegin()const
		{
			return _start;
		}
		const_iterator cend()const
		{
			return _finish;
		}

 迭代器这里没什么大问题,直接用就行。

4.析构函数

		~vector()
		{
			if (_start)
			{
				delete[] _start;
				_start = _finish = _end_of_storage;
			}
		}

 为空就没有可释放的资源。

、空间增长接口

        size_t capacity()const
		{
			return _end_of_storage - _start;
		}
		size_t size()const
		{
			return _finish - _start;
		}
		void reserve(size_t n)
		{
			//只扩容,不缩容
			if (n > capacity())
			{
				size_t sz = size();
				T* tmp = new T[n];
				//如果_start为空,直接把tmp赋值给_start就行,不用释放旧空间
				if (_start)
				{
					//vector是深拷贝,但是vector空间上存一个开空间的自定义类型数组,例:        
                    //string的数组
					//使用memcpy导致string对象的浅拷贝
					//memcpy(tmp, _start, sizeof(T) * size());
					for (size_t i=0;i<size();i++)
					{
						//假设T是string类型,这里调用string的赋值运算符重载,是深拷贝
						tmp[i] = _start[i];
					}
					delete[] _start;
				}
				_start = tmp;
				_finish = tmp + sz;
				_end_of_storage = tmp + n;
			}
		}
		void resize(size_t n, const T& value = T())
		{
			//三种情况
			//n<size
			if (n < size())
			{
				iterator pos = _start + n;
				while (pos != _finish)
				{
					erase(pos);
				}
			}
			//n>容量,要扩容
			else if (n > size())
			{
				//n大于size但小于容量
				if (n > capacity())
				{
					reserve(n);
				}
				while (_finish != _start + n)
				{
					push_back(value);
				}
			}
		}

这里只要注意临界条件就行了。

、增删接口

        void push_back(const T& value)
		{
			满了,扩容
			//if (_finish == _end_of_storage)
			//{
			//	size_t newcapacity = capacity() < 4 ? 4 : capacity() * 2;
			//	reserve(newcapacity);
			//}
			//*_finish = value;
			//++_finish;
			insert(_finish, value);
		}
		void pop_back()
		{
			erase(_finish-1);
		}
		
		iterator insert(iterator pos, const T& x)
		{
			assert(pos <= _finish);

			// 空间不够先进行增容
			if (_finish == _end_of_storage)
			{
				//size_t size = size();
				size_t newCapacity = (0 == capacity()) ? 1 : capacity() * 2;
				reserve(newCapacity);

				// 如果发生了增容,需要重置pos,防止迭代器失效
				pos = _start + size();
			}

			iterator end = _finish - 1;
			while (end >= pos)
			{
				*(end + 1) = *end;
				--end;
			}

			*pos = x;
			++_finish;
			return pos;
		}
		// 返回删除数据的下一个数据
		// 方便解决:一边遍历一边删除的迭代器失效问题
		iterator erase(iterator pos)
		{
			// 挪动数据进行删除
			iterator begin = pos + 1;
			while (begin != _finish) {
				*(begin - 1) = *begin;
				++begin;
			}
			--_finish;
			return pos;
		}

 五、其他

        T& front()
		{
			return *_start;
		}
		T& back()
		{
			return *(_finish - 1);
		}
        T& operator[](size_t pos)
		{
			//判断pos位置合法性
			assert(pos < size());
			return *(_start + pos);
		}
		const T& operator[](size_t pos)const
		{
			//判断pos位置合法性
			assert(pos < size());
			return *(_start + pos);
		}
		

总结

vector实现只要注意迭代器失效的问题和拷贝不能用memcpy这俩问题。

蟹蟹观看!点赞!关注!收藏!评论!一键三连!

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

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

相关文章

数字后端零基础入门系列 | Innovus零基础LAB学习Day8

###LAB15 Detail Routing for Signal Integrity, Timing, Power and Design for Yield 这个章节虽然标题有点长&#xff0c;但不要被它吓到&#xff0c;其实这个章节就是Innovus工具的绕线Routing。只不过这个阶段做Route不是仅仅是把所有的逻辑连接&#xff0c;用实际的金属层…

发现研究的组织是自己所在组织,有点慌

donnyhuang 2019-5-29 23:34 我们做业务建模研究的组织可以是自己所在的组织吗&#xff1f;例如&#xff0c;如果我想做一个组件来来提高研发效能&#xff0c;那我研究的组织是我们部门的研发线可以吗&#xff1f; 因为最近做一个设备管理的建模&#xff0c;发现研究的组织是自…

将数学学生搞糊涂的糊涂话:面积(路程)是一种对应规则(关系)

黄小宁 将一种关系与构成此关系的成员、要素混为一谈是非常低级的常识性错误。问题是学生们做梦都不敢怀疑课本会有这样的错误。有战士与班长之间构成一种上下级关系&#xff0c;战士必须服从上级的指挥。将构成此关系中的班长说成是关系本身显然是概念性错误。同样&#xff0c…

(九)JavaWeb后端开发3——Servlet

目录 1.Servlet由来 2.Servlet快速入门 3.Servlet执行原理 4.Servlet生命周期 1.Servlet由来 在JaveEE API文档中对Servlet的描述是&#xff1a;可以运行在服务器端的微小程序&#xff0c;但是实际上&#xff0c;Servlet就是一个接口&#xff0c;定义了Java类被浏览器访问…

【51单片机】矩阵键盘

学习使用的开发板&#xff1a;STC89C52RC/LE52RC 编程软件&#xff1a;Keil5 烧录软件&#xff1a;stc-isp 开发板实图&#xff1a; 文章目录 矩阵键盘 矩阵键盘 矩阵键盘位于开发板的右下角 在键盘中按键数量较多时&#xff0c;为了减少I/O口的占用&#xff0c;通常将按键…

特朗普概念股分析:DJT和Phunware股价大涨:买还是卖?

猛兽财经核心观点&#xff1a; &#xff08;1&#xff09;特朗普媒体科技集团的股价已经从9月份的低点上涨了280%。 &#xff08;2&#xff09;随着特朗普赢得大选的概率上升&#xff0c;Phunware的股价也在大幅上涨 &#xff08;3&#xff09;这些股票在大选后逆转的可能性越来…

Python | Leetcode Python题解之第522题最长特殊序列II

题目&#xff1a; 题解&#xff1a; class Solution:def findLUSlength(self, strs: List[str]) -> int:def is_subseq(s: str, t: str) -> bool:pt_s pt_t 0while pt_s < len(s) and pt_t < len(t):if s[pt_s] t[pt_t]:pt_s 1pt_t 1return pt_s len(s)ans …

FlinkCDC-MYSQL批量写入

一、运行环境 &#xff08;1&#xff09;Flink&#xff1a;1.17.2 &#xff08;2&#xff09;Scala&#xff1a;2.12.20 &#xff08;3&#xff09;Mysql&#xff1a;5.7.43 ##开启binlog 二、代码示例 思路&#xff1a;通过滚动窗口收集一批数据推给sink消费。binlog日志对…

群控系统服务端开发模式-应用开发-上传配置功能开发

下面直接进入上传配置功能开发&#xff0c;废话不多说。 一、创建表 1、语句 CREATE TABLE cluster_control.nc_param_upload (id int(11) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT 编号,upload_type tinyint(1) UNSIGNED NOT NULL COMMENT 上传类型 1&#xff1a;本站 2&a…

【大语言模型】ACL2024论文-03 MAGE: 现实环境下机器生成文本检测

【大语言模型】ACL2024论文-03 MAGE: 现实环境下机器生成文本检测 目录 文章目录 【大语言模型】ACL2024论文-03 MAGE: 现实环境下机器生成文本检测目录摘要研究背景问题与挑战如何解决核心创新点算法模型实验效果&#xff08;包含重要数据与结论&#xff09;主要参考工作后续优…

C++设计模式结构型模式———组合模式

文章目录 一、引言二、组合模式三、总结 一、引言 组合模式是一种结构型设计模式&#xff0c; 可以使用它将对象组合成树状结构&#xff0c; 并且能像使用独立对象一样使用它们。代码实现中涉及了递归调用。组合模式与传统上的“类与类之间的组合关系”没有关联&#xff0c;不…

【C/C++】qsort函数的学习与使用

零.导言 在之前的文章中&#xff0c;我介绍了冒泡排序&#xff0c;即按ASCII码值把元素从小到大排序&#xff08;文章链接我放在了第五部分&#xff0c;有兴趣的小伙伴可以求看看&#xff09;。而今天我将继续介绍qsort函数&#xff0c;这个函数可以起到和冒泡排序一样的作用&a…

前段(vue)

目录 跨域是什么&#xff1f; SprinBoot跨域的三种解决方法 JavaScript 有 8 种数据类型&#xff0c; 金额的用什么类型。 前段 区别 JQuery使用$.ajax()实现异步请求 Vue 父子组件间的三种通信方式 Vue2 和 Vue3 存在多方面的区别。 跨域是什么&#xff1f; 跨域是指…

【elkb】索引生命周期管理

索引生命周期管理 Index lifecycle management(索引生命周期管理)是elasticsearch提供的一种用于自动管理索引的生命周期的功能。允许使用者定义索引的各个阶段&#xff0c;从创建至删除。并允许使用者在每个阶段定义索引需要执行的特定动作。这些动作包含索引创建&#xff0c…

基于SSM志愿者招募系统的设计

管理员账户功能包括&#xff1a;系统首页&#xff0c;个人中心&#xff0c;用户管理&#xff0c;志愿组织管理&#xff0c;组织信息管理&#xff0c;组织申请管理&#xff0c;志愿活动管理活动报名管理 用户账号功能包括&#xff1a;系统首页&#xff0c;个人中心&#xff0c;…

msys2更换国内源(多个文件(不是3个文件的版本!))

msys2更换国内源 起因排查答案如下mirrorlist.mingw64mirrorlist.ucrt64mirrorlist.mingw32mirrorlist.mingwmirrorlist.clang64mirrorlist.clang32mirrorlist.msys 不想看经过的直接跳到答案 起因 查了很多个教程大部分都是【打开MSYS2软件内的\etc\pacman.d\ 中3个文件&…

Spring Boot框架下的信息学科平台系统架构设计

3系统分析 3.1可行性分析 通过对本基于保密信息学科平台系统实行的目的初步调查和分析&#xff0c;提出可行性方案并对其一一进行论证。我们在这里主要从技术可行性、经济可行性、操作可行性等方面进行分析。 3.1.1技术可行性 本基于保密信息学科平台系统采用Spring Boot框架&a…

基于Python可视化的热门微博数据分析系统

作者&#xff1a;计算机学姐 开发技术&#xff1a;SpringBoot、SSM、Vue、MySQL、JSP、ElementUI、Python、小程序等&#xff0c;“文末源码”。 专栏推荐&#xff1a;前后端分离项目源码、SpringBoot项目源码、SSM项目源码 系统展示 【2025最新】基于pythondjangovueMySQL的热…

ffplay 实现视频流中音频的延迟

ffplay -rtsp_transport tcp -i rtsp://admin:1234qwer192.168.1.64:554/Streaming/Channels/101 -vn -af "adelay5000|5000"在这个命令中&#xff1a; -vn 参数表示只播放音频。 -af "adelay5000|5000" 参数表示将音频延迟5000毫秒&#xff08;即5秒&…

Iceoryx2:高性能进程间通信框架(中间件)

文章目录 0. 引言1. 主要改进2. Iceoryx2 的架构3. C示例代码3.1 发布者示例&#xff08;publisher.cpp&#xff09;3.2 订阅者示例&#xff08;subscriber.cpp&#xff09; 4. 机制比较5. 架构比较6. Iceoryx vs Iceoryx2参考资料 0. 引言 Iceoryx2 是一个基于 Rust 实现的开…