【C++】手撕 Vector类

目录

1,vector类框架

2,vector ()

3,pinrt()

4,vector(int n, const T& value = T())

5,vector(const vector& v)

6,vector(InputIterator first, InputIterator last)

7,~vector()

8,iterator begin()

9,iterator end()

10,size() const

11,capacity() const

12,reserve(size_t n)

13,resize(size_t n, const T& value = T())

14,push_back(const T& x)

15,pop_back()

16,insert(iterator pos, const T& x)

17,erase(iterator pos)

18,empty()

19,operator[](size_t pos)

20,operator= (vector v)

21,总结


上面我们认识了 vector 类,有了一个大概的理解,下面我们来实现一下 vector 类的框架,来更好的熟悉 vector 类,也让我们对其有着更深的理解; 

1,vector类框架

我们先写一个 vector 类的基本框架;

namespace newVector
{template<class T>class vector{public:// Vector的迭代器是一个原生指针typedef T* iterator;private:iterator _start = nullptr; // 指向数据块的开始iterator _finish = nullptr; // 指向有效数据的尾iterator _endOfStorage = nullptr; // 指向存储容量的尾};
}

vector 类里面是可以包含很多类型的,所以我们用模板来表示,以应用各种场景,vector 类里面多用迭代器的方式来表示,vector 的迭代器其实就是一个原生指针;

_start 指向数据块的开始,_finish 指向有效数据的尾,_endOfStorage 指向存储容量的尾;

2,vector ()

vector()
{}

因为我们在构造框架的时候已经给了缺省值,所以可以不用在写了;

可以看到这里已经初始化了;

3,pinrt()

就是打印输出嘛,方便后续测试;

		void pinrt(){for (size_t i = 0; i < size(); i++){cout << _start[i] << " ";}}

4,vector(int n, const T& value = T())

我们都会用,初始化 n 个 value;

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

有人不知道缺省值给个 T()是什么意思,首先 T()是匿名对象,默认就是编译器对内置类型的初始化; 

测试一下:

int main()
{newVector::vector<int> v1(5, 8);v1.pinrt();return 0;
}

现在我们不给初始化的值试试:

int main()
{newVector::vector<int> v1(5);v1.pinrt();return 0;
}

 默认初始化为0;

5,vector(const vector<T>& v)

拷贝构造(深拷贝)

		vector(const vector<T>& v){reserve(v.capacity());//memcpy(_start, v._start, sizeof(T)*v.size());for (size_t i = 0; i < v.size(); i++){_start[i] = v._start[i];}_finish = _start + v.size();}

为什么不用 memcpy 呢,上面那个明显要便捷一点,因为 memcpy 是浅拷贝,如果遇到 T是string类的时候就会因为析构函数多次析构同一块空间而报错,而下面这个挨个赋值是深拷贝,他们两个 _start 不会指向同一块空间;

int main()
{newVector::vector<int> v1(5,8);newVector::vector<int> v2(v1);v2.pinrt();return 0;
}

可以看到也是 OK 的;

6,vector(InputIterator first, InputIterator last)

这个要配合模板使用,这代表一个范围,只要是同类型的都可以;

		template<class InputIterator>vector(InputIterator first, InputIterator last){reserve(last - first + 1);while (first != last){push_back(*first);first++;}}

先扩容嘛,像这种区间都是左闭右开的,然后再挨个尾插即可;

int main()
{int arr[5] = { 1,2,3,4,5 };newVector::vector<int> v1(arr,arr+5);v1.pinrt();return 0;
}

只要是同类型的都可以,像这种的数组都行;

7,~vector()

析构函数

		~vector(){delete[] _start;_start = _finish = _endOfStorage = nullptr;}

delete 后面一定要带 [ ] ,因为析构的是一段连续的空间,就看做是数组即可,然后再将各个迭代器置空即可;

int main()
{newVector::vector<int> v1(5,8);v1.~vector();v1.pinrt();return 0;
}

8,iterator begin()

指向第一个元素的迭代器

		iterator begin(){return _start;}

直接返回 _start 即可;

int main()
{int arr[5] = { 1,2,3,4,5 };newVector::vector<int> v1(arr,arr+5);cout << *v1.begin();return 0;
}

9,iterator end()

指向最后一个元素下一个元素的迭代器

		iterator end(){return _finish;}

直接返回 _finish 即可;

int main()
{int arr[5] = { 1,2,3,4,5 };newVector::vector<int> v1(arr,arr+5);cout << *v1.end();return 0;
}

直接随机数了;

换个思路,试一下:

int main()
{int arr[5] = { 1,2,3,4,5 };newVector::vector<int> v1(arr,arr+5);cout << *(v1.end()-1);return 0;
}

我们找他前一个迭代器,果然是最后一个数;

10,size() const

返回有效数据个数

		size_t size() const{return _finish - _start;}

直接迭代器相减即可;

int main()
{int arr[5] = { 1,2,3,4,5 };newVector::vector<int> v1(arr,arr+5);cout << v1.size();return 0;
}

11,capacity() const

返回容量大小

		size_t capacity() const{return _endOfStorage - _start;}

直接迭代器相减即可,差值就是容量大小;

int main()
{int arr[5] = { 1,2,3,4,5 };newVector::vector<int> v1(arr,arr+5);cout << v1.capacity();return 0;
}

12,reserve(size_t n)

扩容

		void reserve(size_t n){if (n > capacity()){T* tmp = new T[n];size_t old = size();if (_start){//memcpy(tmp, _start, old * sizeof(T));for (size_t i = 0; i < old; i++){tmp[i] = _start[i];}delete[] _start;}_start = tmp;_finish = _start + old;_endOfStorage = _start + n;}}

当要扩容时才用的着,先判断,然后开辟一段需要的空间,之后就是拷贝赋值了,这里我们还是没有用 memcpy 因为是浅拷贝,然后再释放原空间,再将迭代器重新赋值即可;

int main()
{int arr[5] = { 1,2,3,4,5 };newVector::vector<int> v1(arr,arr+5);cout << v1.capacity() << endl;v1.reserve(20);cout << v1.capacity() << endl;return 0;
}

一目了然;

13,resize(size_t n, const T& value = T())

更改有效数据个数,不够则填充,可以指定填充数据;

		void resize(size_t n, const T& value = T()){if (n < size()){_finish = _start + n;}else{if (n > capacity()){reserve(n);}while (_finish!=_start+n){push_back(value);}}}

当缩减数据时直接把 _finish 往前移即可,当扩容时先扩容,然后进行填充;

int main()
{int arr[5] = { 1,2,3,4,5 };newVector::vector<int> v1(arr,arr+5);cout << v1.size() << endl;v1.resize(10);cout << v1.size() << endl;v1.resize(15, 6);cout << v1.size() << endl;v1.pinrt();return 0;
}

可以看到,当我们不指定填充时默认填充0;

14,push_back(const T& x)

尾插

		void push_back(const T& x){if (_finish == _endOfStorage){size_t newcapacity = capacity() == 0 ? 4 : capacity() * 2;reserve(newcapacity);}*_finish = x;_finish++;}

首先要检查是否需要扩容,然后赋值,将 _finish 往后移一位即可;

int main()
{int arr[5] = { 1,2,3,4,5 };newVector::vector<int> v1(arr, arr + 5);v1.push_back(6);v1.push_back(7);v1.pinrt();return 0;
}

 插入十分成功;

15,pop_back()

尾删

		void pop_back(){assert(size() > 0);_finish--;}

像这种数组类型的,直接对其迭代器动手就行,直接 _finish 往前移一位即可;

int main()
{int arr[5] = { 1,2,3,4,5 };newVector::vector<int> v1(arr, arr + 5);v1.pop_back();v1.pop_back();v1.pinrt();return 0;
}

删除非常顺利;

16,insert(iterator pos, const T& x)

指定位置插入

		iterator insert(iterator pos, const T& x){assert(pos >= _start && pos <= _finish);size_t old = pos - _start;if (_finish == _endOfStorage){size_t newcapacity = capacity() == 0 ? 4 : capacity() * 2;reserve(newcapacity);}pos = _start + old;memcpy(pos + 1, pos, (_finish - pos) * sizeof(T));*pos = x;_finish++;return pos;}

这里会面临一个迭代器失效的问题,当扩容后,原本的 pos 还是指向旧空间就失效了,所以我们要更新 pos ;

int main()
{int arr[5] = { 1,2,3,4,5 };newVector::vector<int> v1(arr, arr + 5);auto pos = find(v1.begin(), v1.end(), 1);v1.insert(pos, 9);pos= find(v1.begin(), v1.end(), 5);v1.insert(pos, 9);v1.pinrt();return 0;
}

完美插入;

17,erase(iterator pos)

擦除指定位置

		iterator erase(iterator pos){assert(pos >= 0 && pos <= _finish);memcpy(pos, pos + 1, sizeof(T)*(_finish - pos));_finish--;return pos;}

先断言判断,然后直接往前移一位覆盖即可,再更新一下 _finish ;

int main()
{int arr[5] = { 1,2,3,4,5 };newVector::vector<int> v1(arr, arr + 5);auto pos = find(v1.begin(), v1.end(), 1);v1.erase(pos);pos = find(v1.begin(), v1.end(), 5);v1.erase(pos);v1.pinrt();return 0;
}

也是成功擦除了;

18,empty()

判空

		bool empty(){return _start == _finish;}

当为空时返回真,反之亦然;

int main()
{int arr[5] = { 1,2,3,4,5 };newVector::vector<int> v1(arr, arr + 5);cout << v1.empty() << endl;newVector::vector<int> v2;cout << v2.empty();return 0;
}

19,operator[](size_t pos)

返回下标对应的值;

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

直接像数组一样取值返回即可;

int main()
{int arr[5] = { 1,2,3,4,5 };newVector::vector<int> v1(arr, arr + 5);cout << v1[0] << " " << v1[4];return 0;
}

写法也是跟数组一样简单;

20,operator= (vector<T> v)

赋值,深拷贝

		void swap(vector<T>& v){std::swap(_start, v._start);std::swap(_finish, v._finish);std::swap(_endOfStorage, v._endOfStorage);}vector<T>& operator= (vector<T> v){swap(v);return *this;}

直接一手交换即可;

int main()
{int arr[5] = { 1,2,3,4,5 };newVector::vector<int> v1(arr, arr + 5);newVector::vector<int> v2 = v1;v2.pinrt();return 0;
}

 

21,总结

我们就先搞一个大概的,其中还有很多分支,比如我们写的是擦除某个数据,其实也可以擦除某个范围,这些就靠大家去摸索,查阅文档了;

vector类的实现就到这里了;

加油!

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

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

相关文章

Windows磁盘空间占用分析工具-WizTree

文章目录 WizTree作用WizTree树状分析图WizTree特点获取网址 WizTree作用 平时我们电脑用久了&#xff0c;产生很多文件&#xff0c;导致盘符空间不足&#xff0c;但是不知道那些文件占用比较多&#xff0c;这就需要磁盘空间分析工具-WizTree来分析文件占用情况 WizTree树状分…

计算机组成原理实训

文章目录 一、指令操作1、PC程序计数器2、目标编码3、总线规则4、算数运算5、逻辑运算6、源编码7、微变址 二、编码和微地址1、指令操作2、我的操作 问题描述 一、指令操作 1、PC程序计数器 PC装载&#xff08;E/M IP 00&#xff09;&#xff1a; 当 E/M IP 设置为 00 时&…

【ROS2】MOMO的鱼香ROS2(三)ROS2入门篇——ROS2第一个节点

ROS2第一个节点 引言1 认识ROS2节点1.1 节点之间的交互1.2 节点的命令行指令1.3 工作空间1.4 功能包1.4.1 功能包获取安装1.4.2 功能包相关的指令 ros2 pkg 2 ROS2构建工具—Colcon2.1 安装Colcon2.2 测试编译2.3 Colcon其他指令 3 使用RCLPY编写节点3.1 创建Python功能包3.2 编…

2023-12-14 LeetCode每日一题(用邮票贴满网格图)

2023-12-14每日一题 一、题目编号 2132. 用邮票贴满网格图二、题目链接 点击跳转到题目位置 三、题目描述 给你一个 m x n 的二进制矩阵 grid &#xff0c;每个格子要么为 0 &#xff08;空&#xff09;要么为 1 &#xff08;被占据&#xff09;。 给你邮票的尺寸为 stam…

快速上手:探索Spring MVC的学习秘籍!

SpringMVC概述 1&#xff0c;SpringMVC入门案例1.2 案例制作步骤1:创建Maven项目步骤2:补全目录结构步骤3:导入jar包步骤4:创建配置类步骤5:创建Controller类步骤6:使用配置类替换web.xml步骤7:配置Tomcat环境步骤8:启动运行项目步骤9:浏览器访问步骤10:修改Controller返回值解…

单片机数据发送程序

#include<reg51.h> //包含单片机寄存器的头文件 /***************************************************** 函数功能&#xff1a;向PC发送一个字节数据 ***************************************************/ void Send(unsigned char dat) { SBUFdat; whil…

【新手向】VulnHub靶场MONEYBOX:1 | 详细解析

MONEYBOX:1 安装靶机 作为一名新手&#xff0c;首先要配置好环境&#xff0c;才能进行下一步的操作。 将下载的ova文件导入VirtualBox。 VirtualBox下载地址&#xff1a;https://www.oracle.com/cn/virtualization/technologies/vm/downloads/virtualbox-downloads.html 选择…

手把手教你如何配置 AWS WAF 入门

文章目录 1 前言2 新手第一步3 实践3.1 了解托管规则3.2 编写自己的DIY规则3.3 配置实战A&#xff0c;控制泛洪攻击&#xff08;攻击请求速率&#xff09;3.4 配置实战B&#xff1a;当检查到特定路径请求的时候拒绝对方的试探 4 更进一步4.1 什么是合理的规则设计&#xff1f;如…

Leetcode每日一题:1599.经营摩天轮的最大利润

前言&#xff1a;本题是一道逻辑细节题&#xff0c;考察阅读理解并转化为代码的能力&#xff0c;很多细节 题目描述&#xff1a; 你正在经营一座摩天轮&#xff0c;该摩天轮共有 4 个座舱 &#xff0c;每个座舱 最多可以容纳 4 位游客 。你可以 逆时针 轮转座舱&#xff0c;但…

基于图神经网络的动态物化视图管理

本期 Paper Reading 主要介绍了发布于 2023 年 ICDE 的论文《Dynamic Materialized View Management using Graph Neural Network》&#xff0c;该文研究了动态物化视图管理问题&#xff0c;提出了一个基于 GNN 的模型。在真实的数据集上的实验结果表明&#xff0c;取得了更高的…

redis 从0到1完整学习 (十二):RedisObject 之 List 类型

文章目录 1. 引言2. redis 源码下载3. redisObject 管理 List 类型的数据结构3.1 redisObject 管理 List 类型3.2 List PUSH 源码 4. 参考 1. 引言 前情提要&#xff1a; 《redis 从0到1完整学习 &#xff08;一&#xff09;&#xff1a;安装&初识 redis》 《redis 从0到1…

鸿蒙OS应用开发之气泡提示

前面学习了弹窗提示,其实有时候只是想在旁边做一些说明,那么采用弹窗的方式就比较麻烦一些,这时可以采用系统里面的气泡提示方式。 系统也提供了几种方式弹出气泡提示,最简单的一种是采用bindPopup属性。它的定义如下: 在后面的参数设置里,也是比较复杂的形式。我们先来演…

内存泄漏检测工具

1. vs/vc(windows下)自带的检测工具 将下面的语句加到需要调试的代码中 #define _CRTDBG_MAP_ALLOC // 像一个开关,去开启一些功能,这个必须放在最上面 #include <stdlib.h> #include <crtdbg.h>// 接管new操作符 原理: 就是使用新定义的DBG_NEW去替换代码中的n…

Jetpack Compose中使用Android View

使用AndroidView创建日历 Composable fun AndroidViewPage() {AndroidView(factory {CalendarView(it)},modifier Modifier.fillMaxWidth(),update {it.setOnDateChangeListener { view, year, month, day ->Toast.makeText(view.context, "${year}年${month 1}月$…

x-cmd pkg | openssl - 密码学开源工具集

目录 简介首次用户技术特点竞品分析进一步阅读 简介 OpenSSL 是一个开源的密码库和 SSL/TLS 协议实现&#xff0c;它提供了一组密码学工具和加密功能&#xff0c;用于保护数据通信的安全性。项目发展历史可以追溯到 1998 年&#xff0c;源自 Eric A. Young 和 Tim J. Hudson 开…

Docker的一个简单例子(一)

文章目录 环境示例准备构建启动/停止容器更新应用分享应用 参考 环境 RHEL 9.3Docker Community 24.0.7 示例 准备 从github克隆 getting-started-app 项目&#xff1a; git clone https://github.com/docker/getting-started-app.git查看项目&#xff1a; ➜ getting-s…

I.MX8QM flexcan移植

Android SDK&#xff1a;imx8_13.0.0_1.2.0(android 13 u-boot 2022.04 kernel 5.15.74) 一、kernel 内核配置&#xff1a; # 相应的defconfig中添加使能下面两个宏。 # 官方默认的配置可能是以模块的方式编译&#xff0c;这里直接将can驱动编译到内核中 CONFIG_CANy CONFIG…

C++面试宝典第12题:数组元素相除

题目 从控制台输入若干个整数作为数组,将数组中每一个元素除以第一个元素的结果,作为新的数组元素值。比如:可以先输入3,作为数组元素的个数;然后输入3个整数,作为数组元素的值。 解析 这道题本身并不复杂,但里面隐藏了不少“坑点”和“雷区”,主要考察应聘者全面思考问…

vue项目中实现预览pdf

vue项目中实现预览pdf 1. iframe <iframe :src"pdfSrc"></iframe> ​data() {return {pdfSrc: http://192.168.0.254:19000/trend/2023/12/27/5635529375174c7798b5fabc22cbec45.pdf,}},​iframe {width: 100%;height: calc(100vh - 132px - 2 * 20px -…

数据结构:堆的三部曲 (一)堆的实现

堆的实现 1.堆的结构1.1堆的定义理解 2.堆的实现&#xff08;以小根堆为例&#xff09;2.1 堆结构体的定义2.2 堆的插入交换函数向上调整算法插入函数的代码 2.3 堆的删除向下调整算法&#xff1a;删除函数的代码&#xff1a; 2.4其他操作 3.测试以及完整源代码实现3.1测试代码…