vector 简单模拟实现

 

目录

一. vector成员变量

 二. vector的构造函数和析构函数

三. vector的成员函数

1. 容量操作函数

扩容操作

(1). finish更新问题

(2). 扩容深浅拷贝问题

 resize与尾插、尾删与判空

 insert与erase与clear

2. 函数重载

(1). 赋值运算符重载

(2). [ ]重载进行访问

四. 完整代码


 vector的底层实现主要依赖于动态数组,特点是可以根据需要自动扩展和收缩

我们在这里将其封装到一个命名空间中来实现

一. vector成员变量

vector成员变量是三个迭代器,这里我们用指针typedef实现

#include<iostream>
#include<assert.h>
#include<cstring>
using  namespace std;
namespace Pc
{template<typename T>class vector{public:typedef T* iterator;typedef const T* const_iterator;//......private:iterator _start = nullptr;iterator _finish = nullptr;iterator _end_of_storage = nullptr;};

我们再实现begin()和end()来返回迭代器(代码量小的都是在类内定义的)

		iterator begin(){return _start;}iterator end(){return _finish;}const_iterator begin() const{return _start;}const_iterator end() const{return _finish;}

 二. vector的构造函数和析构函数

分别是默认构造函数,用迭代器构造,还有构造将n个val给予数组以及拷贝构造函数

		vector()//什么也不写,成员变量走了初始化列表{}//vector() = default;//关键字,强制生成构造函数template<typename InputIterator>//这样就可以用任意的迭代器初始化,要求类型是匹配的vector(InputIterator first,InputIterator last){while (first!=last){push_back(*first);first++;}}vector(size_t n, const T& val = T()){reserve(n);//提前开好空间,不然用别的迭代器会有问题for (size_t i = 0; i < n; i++){push_back(val);}}vector(int n, const T& val = T()){reserve(n);//提前开好空间,不然用别的迭代器会有问题for (size_t i = 0; i < n; i++){push_back(val);}}vector(const vector<T>& v){reserve(size());for (auto& e : v){push_back(e);//直接尾插即可}}~vector(){if (_start){delete[] _start;_start = _finish = _end_of_storage = nullptr;}}

const T& val = T(),给缺省值使自定义类型走构造函数,内置类型int等也有默认构造的概念(当然也有析构)

如下代码

		int i = int(); //内置类型int等也有默认构造的概念,当然也有析构int j = int(1);int k(2);

为什么使用迭代器进行构造使用模版函数呢,因为这样就可以使用任意类型的迭代器初始化,要求类型匹配,但这样也有问题

例如以下测试用例

	void test_vector5(){vector<int> v1;v1.push_back(1);v1.push_back(1);v1.push_back(1);v1.push_back(2);v1.push_back(2);v1.push_back(2);vector<int> v2(v1.begin() , v1.end());vector_printf(v1);vector_printf(v2);list<int> lt;lt.push_back(10);lt.push_back(10);lt.push_back(10);lt.push_back(10);vector<int> v3(lt.begin(), lt.end());vector<string> v4(10, "11111");vector<string> v5(10, "");vector_printf(v3);vector_printf(v4);vector_printf(v5);vector<int> v6(10, 1);//因为两个参数一样会调用vector(InputIterator first,InputIterator last)//而非 vector(size_t n, const T& val = T())//解决方法1. 将第一个参数强转,2. 将函数重载第一个参数为int类型 vector_printf(v6);}

因为两个参数一样会调用vector(InputIterator first,InputIterator last)而非 vector(size_t n, const T& val = T())

此时我们我们可以

1. 将第一个参数强转

2. 将函数重载第一个参数为int类型,即我们上面的代码

三. vector的成员函数

1. 容量操作函数
扩容操作
		size_t size() const//求元素个数{return _finish - _start;}size_t capacity() const//求vector容量{return _end_of_storage - _start;}void reserve(size_t n){if (n > capacity()){size_t old_size = _finish - _start;T* tmp = new T[n];//memcpy(tmp, _start, sizeof(T) * size());//浅拷贝,string之类的释放就为随机值了for (size_t i = 0; i < old_size; i++){tmp[i]= _start[i] ; }delete[] _start;_start = tmp;_finish = old_size + _start;_end_of_storage = _start + n;}}
(1). finish更新问题

在开辟新空间后原有的空间就释放了所以我们不可以在释放空间后将_finish的值更新写为

_finish=tmp+size();

 因为size()的实现是要用到已经被释放的_finish的,我们可以

1. 将_finish的更改写到delete[] 即释放空间前面

2. 用一个数记录size(),使_finish再空间释放后加这个数

(2). 扩容深浅拷贝问题

这里我们为什么不用memcpy呢

1. memcpy是内存的二进制格式拷贝将一段内存空间中的内容原封不动的拷贝到另外一段内存空间中

2. . 如果拷贝的是内置类型的元素,memcpy既高效又不会出错,但如果拷贝的是自定义类型 元素,并且自定义类型元素中涉及到资源管理时,就会出错,因为memcpy的拷贝实际是浅拷贝

 比如存储的是string或者是vector,进行了浅拷贝后就被释放了,那相当于存储了几个被释放的空间

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


 resize与尾插、尾删与判空
		//自定义类型会调用默认构造,为了兼容 内置类型int等也有默认构造的概念void resize(size_t n, T val = T())//const T& val=T(){if (n < size()){_finish = _start + n;}else{reserve(n);while (_finish != _start + n){*_finish = val;_finish++;}}}void push_back(const T& x){if (_finish == _end_of_storage){reserve(capacity() == 0 ? 4 : 2 * capacity());}*_finish = x;_finish++;}void pop_back(){assert(!empty());_finish--;}bool empty(){return _start == _finish;}

上面是resize是调整容器大小使其包含n个元素

n<容器大小则内容将减少到其前 n 个元素,并删除超出其范围的元素(并销毁它们)

n 大于当前容器大小,则通过在末尾插入尽可能多的元素来扩展内容,以达到 n 的大小

大于容器大小会扩容到指定大小

剩下的为尾插尾删和判断vector是否为空 


 insert与erase与clear
		void insert(iterator pos, const T& x){assert(pos - _start <= size());if (_finish == _end_of_storage){reserve(capacity() == 0 ? 4 : 2 * capacity());}iterator end = _finish;while (end > pos){*end = *(end - 1);end--;}*end = x;_finish++;}void erase(iterator pos){assert(pos - _finish < 0);iterator end = pos;while (end != _finish){*end = *(end + 1);end++;}_finish--;}void clear(){_finish = _start;}

需要注意的还是迭代器失效问题,如果使用迭代器需要在扩容操作后及时更新

2. 函数重载
(1). 赋值运算符重载
		//vector<T>& operator=(const vector<T>& v)//{//	if (this != &v)//若,地址相同说明是自己给自己赋值,什么也不用干//	{//		clear();//		reserve(v.size());//		for (auto& e : v)//		{//			push_back(e);//		}//	}//	//if (this != &v)//	//{//	//	vector<T> tmp(v);//	//	swap(tmp);//	//}//	return *this;//}void swap(vector<T>& v){std::swap(_start, v._start);std::swap(_finish, v._finish);std::swap(_end_of_storage, v._end_of_storage);}//vector& operator=(vector v)//类内允许这样,类外不可以vector<T>& operator=(vector<T> v){swap(v);return *this;}

我们有两种方法三种方法

1. 参数不传引用通过自己实现的交换函数,与v这个拷贝构造的对象交换,v在函数结束生命周期就结束了

2. 参数传引用判断赋值的是不是自己,是就直接返回,不是就清空vector后一个个尾插

3. 参数传引用,手动拷贝构造一个vector类对象与自身交换

(2). [ ]重载进行访问

实现可读可写与可读不可写两种,返回引用减少拷贝消耗

		T& operator[](size_t pos){assert(pos < size());return _start[pos];}T& operator[](size_t pos) const{assert(pos < size());return _start[pos];}

四. 完整代码

#include<iostream>
#include<assert.h>
#include<cstring>
#include<list>
using  namespace std;
namespace Pc
{template<typename T>class vector{public: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;}vector()//什么也不写,成员变量走了初始化列表{}//vector() = default;//关键字,强制生成构造函数template<typename InputIterator>//这样就可以用任意的迭代器初始化,要求类型是匹配的vector(InputIterator first,InputIterator last){while (first!=last){push_back(*first);first++;}}vector(size_t n, const T& val = T()){reserve(n);//提前开好空间,不然用别的迭代器会有问题for (size_t i = 0; i < n; i++){push_back(val);}}vector(int n, const T& val = T()){//reserve(n);//提前开好空间,不然用别的迭代器会有问题for (size_t i = 0; i < n; i++){push_back(val);}}vector(const vector<T>& v){//reserve(size());for (auto& e : v){push_back(e);//直接尾插即可}}//vector<T>& operator=(const vector<T>& v)//{//	if (this != &v)//若,地址相同说明是自己给自己赋值,什么也不用干//	{//		clear();//		reserve(v.size());//		for (auto& e : v)//		{//			push_back(e);//		}//	}//	//if (this != &v)//	//{//	//	vector<T> tmp(v);//	//	swap(tmp);//	//}//	return *this;//}void swap(vector<T>& v){std::swap(_start, v._start);std::swap(_finish, v._finish);std::swap(_end_of_storage, v._end_of_storage);}//vector& operator=(vector v)//类内允许这样,类外不可以vector<T>& operator=(vector<T> v){swap(v);return *this;}~vector(){if (_start){delete[] _start;_start = _finish = _end_of_storage = nullptr;}}void reserve(size_t n){if (n > capacity()){size_t old_size = _finish - _start;T* tmp = new T[n];//memcpy(tmp, _start, sizeof(T) * size());//浅拷贝,string之类的释放就为随机值了for (size_t i = 0; i < old_size; i++){tmp[i]= _start[i] ; }delete[] _start;_start = tmp;_finish = old_size + _start;_end_of_storage = _start + n;}}bool empty(){return _start == _finish;}//自定义类型会调用默认构造,为了兼容 内置类型int等也有默认构造的概念void resize(size_t n, T val = T())//const T& val=T(){if (n < size()){_finish = _start + n;}else{reserve(n);while (_finish != _start + n){*_finish = val;_finish++;}}}size_t size() const{return _finish - _start;}size_t capacity() const{return _end_of_storage - _start;}void push_back(const T& x){if (_finish == _end_of_storage){reserve(capacity() == 0 ? 4 : 2 * capacity());}*_finish = x;_finish++;}void pop_back(){assert(!empty());_finish--;}void insert(iterator pos, const T& x){assert(pos - _start <= size());if (_finish == _end_of_storage){reserve(capacity() == 0 ? 4 : 2 * capacity());}iterator end = _finish;while (end > pos){*end = *(end - 1);end--;}*end = x;_finish++;}void erase(iterator pos){assert(pos - _finish < 0);iterator end = pos;while (end != _finish){*end = *(end + 1);end++;}_finish--;}void clear(){_finish = _start;}T& operator[](size_t pos){assert(pos < size());return _start[pos];}T& operator[](size_t pos) const{assert(pos < size());return _start[pos];}private:iterator _start = nullptr;iterator _finish = nullptr;iterator _end_of_storage = nullptr;};

这篇就到这里啦

(๑′ᴗ‵๑)I Lᵒᵛᵉᵧₒᵤ❤

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

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

相关文章

c++ - unordered_set与unordered_map模拟实现

文章目录 前言一、unordered_set模拟实现二、unordered_map模拟实现 前言 1、unordered_set与unordered_map的介绍与接口使用可参考&#xff1a;unordered_set 、 unordered_map。 2、unordered_set和 unordered_map 的底层实现都是基于哈希表的。哈希表是一种通过哈希函数组织…

LoRa无线通讯,让光伏机器人实现无“线”管理

光伏清洁机器人&#xff0c;作为光伏电站运维的新兴关键设备&#xff0c;已跃升为继组件、支架、光伏逆变器之后的第四大核心组件&#xff0c;正逐步成为光伏电站的标准配置。鉴于光伏电站普遍坐落于偏远无人区或地形复杂之地&#xff0c;光伏清洁机器人必须具备远程操控能力、…

深入源码P3C-PMD:rule (4)

系列文章目录 文章目录 系列文章目录rule 的应用类别 rule rule 自定义XML rule 定义Tree 漫游错误报告生命周期 designer rule相关的代码在每个子 module 的 rule 文件夹。而且也以一些 ruleset 为范围分了文件夹&#xff0c;如下图所示&#xff1a; 对每个 rule 来说&#xf…

PHP教育培训小程序系统源码

&#x1f680;【学习新纪元】解锁教育培训小程序的无限可能✨ &#x1f4da; 引言&#xff1a;教育培训新风尚&#xff0c;小程序来引领&#xff01; Hey小伙伴们&#xff0c;是不是还在为找不到合适的学习资源而烦恼&#xff1f;或是厌倦了传统教育模式的单调&#xff1f;今…

盘点12款企业常用源代码加密软件,源代码防泄密很重要!

在当今的商业环境中&#xff0c;源代码作为企业的核心资产之一&#xff0c;其安全性不容忽视。源代码的泄露可能导致企业丧失竞争优势、面临法律诉讼甚至经济损失。因此&#xff0c;选择合适的源代码加密软件成为企业保护知识产权和核心技术的关键步骤。 1. 安秉源代码加密软件…

【JVM】Java内存区域图文详解

1.JVM运行时区域总览 Java 虚拟机在执行 Java 程序的过程中会把它管理的内存划分成若干个不同的数据区域。 JVM运行时区域也成为Java内存区域。 在讨论Java内存模型时&#xff0c;通常将其分为线程共享区域和线程私有区域&#xff1a; 2.线程私有区域 2.1.程序计数器 程序计…

详解贪心算法

贪心算法&#xff08;Greedy Algorithm) 概述&#xff1a; 贪心算法是一种在求解最优化问题时采取的一种常用算法策略。贪心算法的基本思想是&#xff0c;每次选择当前情况下的局部最优解&#xff0c;并相信这个局部最优解能够导致全局最优解。贪心算法通过迭代的方式一步步地…

SpringBoot3里的文件上传

需求分析&#xff1a; 在用户更换头像或者添加文章时&#xff0c;都需要携带一个图片的URL访问地址。当用户访问文件上传接口将图片的数据上传成功后&#xff0c;服务器会返回一个地址。我们后台需要提供一个文件上传的接口&#xff0c;用来接收前端提交的文件的数据并且返回文…

七夕情人节礼物有哪些走心礼物推荐,盘点惊喜爆棚的四大礼物分享

随着浪漫的七夕情人节的临近&#xff0c;恋人们开始寻找那些能够传达他们挚爱与深情的独特礼物&#xff0c;一份有心的礼物不仅能够成为情感的使者&#xff0c;还能在这一天为爱情增添更多甜蜜与回忆&#xff0c;在这个充满传说与浪漫的节日里&#xff0c;我们精心挑选了一系列…

查询表信息时有一个数据为null相关解决

查询的时候varchar类型的username一直查不到为null,这个问题干了我好久 当时我以为是连接mysql数据库的时候没有在url后面添加添加指定字符的编码、解码格式的参数约束.然后经过分析发现 我创建的这个Account对象 直接上结果&#xff0c;问题出在了setUsername()方法上 错误…

Scrapy入门篇

免责声明 本文的爬虫知识仅用于合法和合理的数据收集&#xff0c;使用者需遵守相关法律法规及目标网站的爬取规则&#xff0c;尊重数据隐私&#xff0c;合理设置访问频率&#xff0c;不得用于非法目的或侵犯他人权益。因使用网络爬虫产生的任何法律纠纷或损失&#xff0c;由使用…

用Java手写jvm之模拟方法调用指令invokexxx和方法返回指令xreturn

写在前面 源码 。 本文一起看下方法调用相关的指令invokexxx以及方法返回&#xff08;栈帧弹出线程栈&#xff09;相关的指令xReturn 。 1&#xff1a;正文 因为invokexxx指令和普通的指令不同&#xff0c;会创建一个新的栈帧&#xff0c;并压倒操作数栈中&#xff0c;所以我…

【python】Python中实现定时任务常见的几种方式原理分析与应用实战

✨✨ 欢迎大家来到景天科技苑✨✨ &#x1f388;&#x1f388; 养成好习惯&#xff0c;先赞后看哦~&#x1f388;&#x1f388; &#x1f3c6; 作者简介&#xff1a;景天科技苑 &#x1f3c6;《头衔》&#xff1a;大厂架构师&#xff0c;华为云开发者社区专家博主&#xff0c;…

【OpenCV C++20 学习笔记】腐蚀和膨胀

腐蚀和膨胀 形态学原理膨胀腐蚀 代码实现膨胀函数腐蚀函数运行结果 形态学原理 腐蚀和膨胀通常有以下用途&#xff1a; 去除噪音分离或合并图像中的元素找出图片上的强度的极大值区域和极小值区域 以下图作为原始图片&#xff1a; 膨胀 用核 B B B来扫描图像 A A A&#xff…

VBA学习(22):动态显示日历

这是在ozgrid.com论坛上看到的一个贴子&#xff0c;很有意思&#xff0c;本来使用公式是可以很方便在工作表中实现日历显示的&#xff0c;但提问者因其需要&#xff0c;想使用VBA实现动态显示日历&#xff0c;即根据输入的年份和月份在工作表中显示日历。 下面是实现该效果的VB…

喜报!DAP-seq文章6连发,总IF 95.2

2024年4月29日&#xff0c;河北农业大学林学院李保国山区产业开发与林果产业创新团队与园艺学院田义教授团队联合西北农林科技大学马锋旺教授团队及沈阳农业大学马跃教授团队在Plant Biotechnology Journal&#xff08;影响因子10.1&#xff09;上发表了题为“The MdVQ37-MdWRK…

2024最新版Python基础入门学习路线

Python基础入门学习路线可以概括为以下几个阶段&#xff0c;每个阶段都包含了关键的学习内容和目标&#xff1a; 一、Python语言基础 1. 初识Python语言 Python语言概述&#xff1a;了解Python的起源、特点、应用领域以及发展趋势。环境安装&#xff1a;学习如何在不同的操作系…

18987 随机数(测验)

这个问题可以通过使用集合&#xff08;set&#xff09;和排序来解决。集合是一种数据结构&#xff0c;它可以自动去除重复的元素。然后我们可以将集合中的元素转移到一个数组中&#xff0c;并对&#xfffd;&#xfffd;组进行排序。 以下是使用C的代码实现&#xff1a; #i…

浅谈哈希与哈希表(c++)

目录 一、哈希的基本概念&#xff08;一&#xff09;哈希函数的特性&#xff08;二&#xff09;哈希冲突 二、C 中的哈希表实现三、哈希表的性能分析四、哈希表的应用场景五、优化哈希表的策略六、例题讲解【模板】字符串哈希题目描述输入格式输出格式样例 #1样例输入 #1样例输…

工业5G路由器赋能户外组网远程监控及预警

随着物联网、大数据、云计算等技术的快速发展&#xff0c;工业领域对于远程监控、实时预警和数据传输的需求日益增长。特别是在户外复杂环境下&#xff0c;传统的有线网络组网方式面临着布线难度大、成本高、维护困难等问题。 工业5G路由器在户外组网远程监控预警应用基于高速…