【C++】手撕 Vector类

news2024/11/30 6:52:03

目录

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.coloradmin.cn/o/1348315.html

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

相关文章

Adobe ColdFusion 文件读取漏洞(CVE-2010-2861)

漏洞原理 Adobe ColdFusion是美国Adobe公司的一款动态Web服务器产品&#xff0c;其运行的CFML&#xff08;ColdFusion Markup Language&#xff09;是针对Web应用的一种程序设计语言。由于AJP协议设计存在缺陷导致内部相关的属性可控&#xff0c;攻击者可以构造属性值&#xff…

C++17中的内联变量

在C11中&#xff1a; (1).声明为constexpr的函数隐式地是内联函数; (2).deleted函数隐式地是一个内联函数。 在内联函数中&#xff1a; 1.所有函数定义中的函数局部静态对象(function-local static object)在所有翻译单元之间共享(它们都引用一个翻译单…

实时交通标志检测和分类(代码)

交通标志检测和分类技术是一种基于计算机视觉和深度学习的先进技术&#xff0c;能够识别道路上的各种交通标志&#xff0c;并对其进行分类和识别。这项技术在智能交通系统、自动驾驶汽车和交通安全管理领域具有重要的应用前景。下面我将结合实时交通标志检测和分类的重要性、技…

【STM32】SPI通信

1 SPI通信 SPI&#xff08;Serial Peripheral Interface&#xff0c;串行外设接口&#xff09;是由Motorola公司开发的一种通用数据总线 四根通信线&#xff1a;SCK&#xff08;Serial Clock&#xff0c;串行时钟&#xff09;、MOSI&#xff08;Master Output Slave Input&am…

【MyBatis】操作数据库——入门

文章目录 为什么要学习MyBatis什么是MyBatisMyBatis 入门创建带有MyBatis框架的SpringBoot项目数据准备在配置文件中配置数据库相关信息实现持久层代码单元测试 为什么要学习MyBatis 前面我们肯定多多少少学过 sql 语言&#xff0c;sql 语言是一种操作数据库的一类语言&#x…

BLE Mesh蓝牙组网技术详细解析之Lower Transport Layer下传输层(四)

目录 一、什么是BLE Mesh Lower Transport Layer下传输层&#xff1f; 二、未分段消息 2.1 未分段接入层消息 2.2 未分段控制层消息 三、分段消息 3.1 超过多少个字节需要分段&#xff1f; 3.2 分段接入层消息 3.3 分段控制层消息 3.4 分段确认消息 3.5 分段和重组流程…

按行依次处理数据的文件操作(C语言版)

按行依次处理数据的文件操作(C语言版) 这段代码的目的是处理多个文件&#xff0c;为每个文件创建一个新文件&#xff0c;将以 ‘r’ 开头的行添加 “./” 前缀&#xff0c;并将修改后的内容写入新文件。在main函数中&#xff0c;通过调用process函数&#xff0c;逐个处理了一系…

牛客网SQL训练5—SQL大厂面试真题

文章目录 一、某音短视频1.各个视频的平均完播率2.平均播放进度大于60%的视频类别3.每类视频近一个月的转发量/率4.每个创作者每月的涨粉率及截止当前的总粉丝量5.国庆期间每类视频点赞量和转发量6.近一个月发布的视频中热度最高的top3视频 二、用户增长场景&#xff08;某度信…

[react]脚手架create-react-app/vite与reac项目

[react]脚手架create-react-app/vite与reac项目 环境问题描述create-react-app 脚手架根据脚手架修改项目结构安装脚手架注入配置文件-config文件夹package.json文件变更删除 serviceWorker.js新增reportWebVitals.js文件更新index.js文件 脚手架creat-react-app 缺点 vite 脚手…

数据结构期末复习(2)链表

链表 链表&#xff08;Linked List&#xff09;是一种常见的数据结构&#xff0c;用于存储一系列具有相同类型的元素。链表由节点&#xff08;Node&#xff09;组成&#xff0c;每个节点包含两部分&#xff1a;数据域&#xff08;存储元素值&#xff09;和指针域&#xff08;指…

MyBatis多表映射

1. 多表映射概念 MyBatis 思想是&#xff1a;数据库不可能永远是你所想或所需的那个样子。 我们希望每个数据库都具备良好的第三范式或 BCNF 范式&#xff0c;可惜它们并不都是那样。 如果能有一种数据库映射模式&#xff0c;完美适配所有的应用程序查询需求&#xff0c;那就太…

【FileZilla的安装与使用(主动与被动模式详解,以及如何利用FileZilla搭建FTP服务器并且进行访问)】

目录 一、FileZilla介绍 1.1 简介 1.2 重要信息和功能 二、FileZilla的安装与使用 2.1 FileZilla服务端安装与配置 2.1.1 安装步骤 2.1.2 新建组 2.1.3 新建用户 2.1.4 新建目录 2.1.5 权限分配 &#xff08;1&#xff09;用户Milk权限分配 &#xff08;2&#xff…

HikvisionCamera开发-萤石云RTMP协议获取视频流

RTMP/RTSP&#xff08;实时流传输协议&#xff09;是一种网络协议&#xff0c;旨在用于传输音频和视频数据。本文将介绍如何在HikvisionCamera二次开发中如何通过RTMP协议获得实时视频流&#xff0c;使用到的摄像头为POE供电的海康威视-臻全彩款&#xff0c;以及套餐内配套录像…

JMeter使用

目录 启动JMeter 创建线程组 设置线程参数 设置http请求参数 ​编辑 创建查看结果树(显示成功/失败多少以及返回结果等信息) 创建聚合报告(显示响应时间、吞吐量、异常数等信息) 点击上方的执行按钮即可开始压力测试 结果树显示 聚合报告结果显示 启动JMeter 在JMete…

【NLP论文】03 基于 jiagu 的情感分析

本篇是NLP论文系列的最后一篇&#xff0c;主要介绍如何计算情感分析结果&#xff0c;并将其融入到XX评价体系和物流关键词词库&#xff0c;之前我已经写了两篇关于情感分析的文章&#xff0c;分别是 SnowNLP 和 Cemotion 技术&#xff0c;最终我才用了 jiagu 来写我的论文&…

机器人中的数值优化之线性共轭梯度法

欢迎大家关注我的B站&#xff1a; 偷吃薯片的Zheng同学的个人空间-偷吃薯片的Zheng同学个人主页-哔哩哔哩视频 (bilibili.com) 本文ppt来自深蓝学院《机器人中的数值优化》 目录 1.无约束优化方法对比 2.Hessian-vec product 3.线性共轭梯度方法的步长​编辑 4.共轭梯度…

mac上使用Navicat Premium 在本地和生产环境中保持数据库同步

Navicat Premium 是一款功能强大的数据库管理和开发工具&#xff0c;支持多种数据库系统&#xff0c;如 MySQL、Oracle、SQL Server 等。作为程序员&#xff0c;我深知在开发过程中需要一款方便、高效的数据库管理工具来提升工作效率。而 Navicat Premium 正是这样一款不可多得…

Spring Boot学习随笔- Jasypt加密数据库用户名和密码以及解密

学习视频&#xff1a;【编程不良人】2021年SpringBoot最新最全教程 第十九章、Jasypt加密 Jasypt全称是Java Simplified Encryption&#xff0c;是一个开源项目。 Jasypt与Spring Boot集成&#xff0c;以便在应用程序的属性文件中加密敏感信息&#xff0c;然后在应用程序运行…

怎么解决 Nginx反向代理加载速度慢?

Nginx反向代理加载速度慢可能由多种原因引起&#xff0c;以下是一些可能的解决方法&#xff1a; 1&#xff0c;网络延迟&#xff1a; 检查目标服务器的网络状况&#xff0c;确保其网络连接正常。如果目标服务器位于不同的地理位置&#xff0c;可能会有较大的网络延迟。考虑使用…

从0到1快速入门ETLCloud

一、ETLCloud的介绍 ETL是将业务系统的数据经过抽取&#xff08;Extract&#xff09;、清洗转换&#xff08;Transform&#xff09;之后加载&#xff08;Load&#xff09;到数据仓库的过程&#xff0c;目的是将企业中的分散、凌乱、标准不统一的数据整合到一起&#xff0c;为企…