【STL】:vector的模拟实现

news2024/12/23 8:19:19

朋友们、伙计们,我们又见面了,本期来给大家解读一下有关vector的模拟实现,如果看完之后对你有一定的启发,那么请留下你的三连,祝大家心想事成!

C 语 言 专 栏:C语言:从入门到精通

数据结构专栏:数据结构

个  人  主  页 :stackY、

C + + 专 栏   :C++

Linux 专 栏  :Linux

目录

1. 基本构造

2. 容量相关的接口

2.1 operator[ ]

2.2 reserve

2.3 resize

2.4 size、capacity

3. 迭代器

4. 修改相关接口

4.1 insert、push_back

4.2 erase

5. 拷贝构造和赋值重载和其他构造

5.1 拷贝构造

5.2 赋值重载

5.3 其他构造

6. 完整代码


1. 基本构造

在实现vector之前可以先看一下库里面的vector是如何实现的:

#pragma once
#include <assert.h>

namespace ywh
{
	template<class T>
	class vector
	{
		typedef T* iterator;
		typedef const T* const_iterator;
	public:
		/基本构造///
		//构造
		vector()
		{}
		//析构
		~vector()
		{
			delete[] _start;
			_start = _finish = _end_of_storage = nullptr;
		}
	private:
		iterator _start = nullptr;   //起始
		iterator _finish = nullptr;  //有效数据个数
		iterator _end_of_storage = nullptr; //有效空间
	};
}

2. 容量相关的接口

2.1 operator[ ]

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

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

2.2 reserve

关于reserve的实现最主要的就是深拷贝与浅拷贝的问题,使用浅拷贝对于内置类型很容易处理,但是对于自定义类型就会出现麻烦:

需要进行深拷贝,关于自定义类型的赋值重载就是一个深拷贝,所以我们需要使用深拷贝来说实现reserve:

//reserve
		void reserve(size_t n)
		{
			if (n > capacity())
			{
				size_t sz = size();
				T* tmp = new T[n];
				if (_start)
				{
					//浅拷贝
					//对于自定义类型不能处理
					//memcpy(tmp,_start,sizeof(T)*sz);
					//采用深拷贝
					for (size_t i = 0; i < sz; i++)
					{
						tmp[i] = _start[i];
					}
					delete[] _start;
				}	
				_start = tmp;
				_finish = _start + sz;
				_end_of_storage = _start + n;
			}
		}

2.3 resize

//resize
		//这里使用匿名对象进行缺省值的传参,由于匿名函数具有常性所以要使用const
		void resize(size_t n, const T& val = T())
		{
			if (n < size())
			{
				_finish = _start + n;
			}
			else
			{
				reserve(n);
				while (_finish < _start + n)
				{
					*_finish = val;
					++_finish;
				}
			}
		}

2.4 size、capacity

有效元素的个数从开始位置到数据截止位置

容量是开始位置到空间终止位置

//容量
		size_t capacity() const
		{
			return _end_of_storage - _start;
		}
		//有效个数
		size_t size() const
		{
			return _finish - _start;
		}

3. 迭代器

关于模拟实现的vector只实现正向迭代器,反向迭代器的实现会在stack和queue的适配器模式中详解

/迭代器
		iterator begin()
		{
			return _start;
		}
		iterator end()
		{
			return _finish;
		}
		const_iterator begin() const
		{
			return _start;
		}
		const_iterator end() const
		{
			return _finish;
		}

4. 修改相关接口

4.1 insert、push_back

在insert的时候需要注意的是:假如需要扩容,那么pos还是在旧空间,所以在扩容之前需要将旧空间pos的偏移量记录出来,然后在扩容之后将新空间的pos记录出来。

//在pos位置插入
		void insert(iterator pos, const T& x)
		{
			assert(pos >= _start);
			assert(pos <= _finish);
			if (_finish == _end_of_storage)
			{
				//记录旧空间中pos的偏移量
				size_t len = pos - _start;
				reserve(capacity() == 0 ? 4 : capacity() * 2);
				//找出新空间的pos
				pos = _start + len;
			}
			iterator end = _finish - 1;
			while (end >= pos)
			{
				*(end + 1) = *end;
				--end;
			}
			*pos = x;
			++_finish;
		}
		//尾插
		void push_back(const T& x)
		{
			//复用插入
			insert(end(), x);
		}

insert之后的迭代器会失效,上述中的pos就是迭代器失效的一种案例,在不同的平台下对于迭代器的失效都有不同的检查方法,VS2019上如果使用insert之后的迭代器是不允许进行使用的。

4.2 erase

	//删除pos位置
		void erase(iterator pos)
		{
			assert(pos >= _start);
			assert(pos < _finish);
			
			iterator it = pos + 1;
			while (it < _finish)
			{
				*(it - 1) = *it;
				++it;
			}
			--_finish;
		}

如果我们单纯的看这个代码会感觉erase之后迭代器是不会失效的,但是erase之后迭代器失效会在特殊情况下展示出来,比如要删除偶数:

void Test()
{
	ywh::vector<int> v;
	v.push_back(1);
	v.push_back(2);
	v.push_back(3);
	v.push_back(4);
	v.push_back(5);
	v.push_back(6);

	ywh::vector<int>::iterator it = v.begin();
	while (it != v.end())
	{
        //删除偶数
		if (*it % 2 == 0)
		{
			v.erase(it);
		}
		it++;
	}

	for (auto e : v)
	{
		cout << e << " ";
	}
	cout << endl;
}

在这里有三种情况:

我们可以看看库里面是如何设计的:

对于返回值的介绍:

返回被删除数据的下一个位置

所以我们需要对我们实现的erase进行调整:

//删除pos位置
		iterator erase(iterator pos)
		{
			assert(pos >= _start);
			assert(pos < _finish);
			
			iterator it = pos + 1;
			while (it < _finish)
			{
				*(it - 1) = *it;
				++it;
			}
			--_finish;
			//返回删除位置的后一个位置
			return pos;
		}
void Test()
{
	ywh::vector<int> v;
	v.push_back(2);
	v.push_back(2);
	v.push_back(3);
	v.push_back(4);
	v.push_back(5);
	v.push_back(6);

	ywh::vector<int>::iterator it = v.begin();
	while (it != v.end())
	{
		if (*it % 2 == 0)
		{
			//删除后自动找到删除位置的下一个位置
			v.erase(it);
		}
		else
		{
			//不符合情况再往后面走
			it++;
		}
	}

	for (auto e : v)
	{
		cout << e << " ";
	}
	cout << endl;
}

5. 拷贝构造和赋值重载和其他构造

5.1 拷贝构造

//拷贝构造
		vector(const vector<T>& v)
		{
            //开辟空间
			reserve(v.capacity());
            //依次尾插
			for (auto e : v)
			{
				push_back(e);
			}
		}

5.2 赋值重载

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<T>& operator=(vector<T> tmp) 
{
	//直接交换
	swap(tmp);
	return *this;
}

5.3 其他构造

//迭代器区间初始化
		template<class InputIterator>
		vector(InputIterator first, InputIterator last)
		{
			while (first != last)
			{
				push_back(*first);
				++first;
			}
		}

		//n个val构造
		vector(size_t n, const T& val = T())
		{
			reserve(n);
			for (size_t i = 0; i < n; i++)
			{
				//n个val进行尾插
				push_back(val);
			}
		}
		vector(int n, const T& val = T())
		{
			reserve(n);
			for (int i = 0; i < n; i++)
			{
				//n个val进行尾插
				push_back(val);
			}
		}

6. 完整代码

头文件:Vector.h

#pragma once
#include <assert.h>

namespace ywh
{
	template<class T>
	class vector
	{
	public:
		typedef T* iterator;
		typedef const T* const_iterator;
	public:
		/迭代器
		iterator begin()
		{
			return _start;
		}
		iterator end()
		{
			return _finish;
		}
		const_iterator begin() const
		{
			return _start;
		}
		const_iterator end() const
		{
			return _finish;
		}

		/基本构造///
		//构造
		vector()
		{}

		//迭代器区间初始化
		template<class InputIterator>
		vector(InputIterator first, InputIterator last)
		{
			while (first != last)
			{
				push_back(*first);
				++first;
			}
		}

		//n个val构造
		vector(size_t n, const T& val = T())
		{
			reserve(n);
			for (size_t i = 0; i < n; i++)
			{
				//n个val进行尾插
				push_back(val);
			}
		}
		vector(int n, const T& val = T())
		{
			reserve(n);
			for (int i = 0; i < n; i++)
			{
				//n个val进行尾插
				push_back(val);
			}
		}

		//拷贝构造
		vector(const vector<T>& v)
		{
			//开辟空间
			reserve(v.capacity());
			//依次尾插
			for (auto e : v)
			{
				push_back(e);
			}
		}


		void swap(vector<T>& v)
		{
			std::swap(_start, v._start);
			std::swap(_finish, v._finish);
			std::swap(_end_of_storage, v._end_of_storage);
		}
        //operator=
		vector<T>& operator=(vector<T> tmp) 
		{
			//直接交换
			swap(tmp);
			return *this;
		}

		//析构
		~vector()
		{
			delete[] _start;
			_start = _finish = _end_of_storage = nullptr;
		}
		///容量
		//operator[]
		T& operator[](size_t pos)
		{
			assert(pos < size());
			return _start[pos];
		}

		const T& operator[](size_t pos) const
		{
			assert(pos < size());
			return _start[pos];
		}
		//容量
		size_t capacity() const
		{
			return _end_of_storage - _start;
		}
		//有效个数
		size_t size() const
		{
			return _finish - _start;
		}
		//reserve
		void reserve(size_t n)
		{
			if (n > capacity())
			{
				size_t sz = size();
				T* tmp = new T[n];
				if (_start)
				{
					//浅拷贝
					//对于自定义类型不能处理
					//memcpy(tmp,_start,sizeof(T)*sz);
					//采用深拷贝
					for (size_t i = 0; i < sz; i++)
					{
						tmp[i] = _start[i];
					}
					delete[] _start;
				}	
				_start = tmp;
				_finish = _start + sz;
				_end_of_storage = _start + n;
			}
		}
		//resize
		//这里使用匿名对象进行缺省值的传参,由于匿名函数具有常性所以要使用const
		void resize(size_t n, const T& val = T())
		{
			if (n < size())
			{
				_finish = _start + n;
			}
			else
			{
				reserve(n);
				while (_finish < _start + n)
				{
					*_finish = val;
					++_finish;
				}
			}
		}

		///修改
		//在pos位置插入
		void insert(iterator pos, const T& x)
		{
			assert(pos >= _start);
			assert(pos <= _finish);
			if (_finish == _end_of_storage)
			{
				//记录旧空间中pos的偏移量
				size_t len = pos - _start;
				reserve(capacity() == 0 ? 4 : capacity() * 2);
				//找出新空间的pos
				pos = _start + len;
			}
			iterator end = _finish - 1;
			while (end >= pos)
			{
				*(end + 1) = *end;
				--end;
			}
			*pos = x;
			++_finish;
		}
		//尾插
		void push_back(const T& x)
		{
			//复用插入
			insert(end(), x);
		}
		//删除pos位置
		iterator erase(iterator pos)
		{
			assert(pos >= _start);
			assert(pos < _finish);
			
			iterator it = pos + 1;
			while (it < _finish)
			{
				*(it - 1) = *it;
				++it;
			}
			--_finish;
			//返回删除位置的后一个位置
			return pos;
		}

	private:
		iterator _start = nullptr;   //起始位置
		iterator _finish = nullptr;  //有效数据位置
		iterator _end_of_storage = nullptr; //结束位置
	};
}

朋友们、伙计们,美好的时光总是短暂的,我们本期的的分享就到此结束,欲知后事如何,请听下回分解~,最后看完别忘了留下你们弥足珍贵的三连喔,感谢大家的支持! 

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

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

相关文章

qt 系列(一)---qt designer设计常用操作

最近转战qt, 主要用qt designer 进行GUI开发&#xff0c;记录下实战经验~ 1.前言 qt 是跨平台C图形用户界面应用程序开发框架&#xff0c;可以使用的IDE工具有 qt creator 和 vs, 这里我主要使用 Visual Studio 2017 工具进行程序开发与编写。 2. 环境配置 只写关键步骤~~ …

Java 谈谈你对OOM的认识

文章目录 前言一、基础架构二、常见OOM1、栈内存溢出java.lang.StackOverflowError2、堆内存溢出java.lang.OutOfMemoryError&#xff1a;Java heap space3、GC回收时间过长java.lang.OutOfMemoryError: GC overhead limit exceeded4、NIO程序堆外内存溢出java.lang.OutOfMemor…

MySQL使用存储过程迁移用户表数据,过滤用户名相同名称不同的用户

存储过程简介 存储过程&#xff08;Stored Procedure&#xff09;是一组为了完成特定功能的SQL语句集&#xff0c;经编译后存储在数据库中&#xff0c;用户通过指定存储过程的名字并给定参数&#xff08;如果该存储过程带有参数&#xff09;来调用执行它。它是一段预编译的SQL…

openGauss学习笔记-110 openGauss 数据库管理-管理用户及权限-Schema

文章目录 openGauss学习笔记-110 openGauss 数据库管理-管理用户及权限-Schema110.1 创建、修改和删除Schema110.2 搜索路径 openGauss学习笔记-110 openGauss 数据库管理-管理用户及权限-Schema Schema又称作模式。通过管理Schema&#xff0c;允许多个用户使用同一数据库而不…

为什么 ConcurrentHashMap 中 key 不允许为 null

ConcurrentHashMap 在ConcurrentHashMap 的源码&#xff0c;在 put 方法里面&#xff0c;可以看到这样一段代码&#xff0c;如果 key 或者 value 为空&#xff0c;则抛出空指针异常。 但是为什么 ConcurrentHashMap 不允许 key 或者 value 为空呢&#xff1f; 原因 简单来说&…

macOS M1安装wxPython报错‘tiff.h‘ file not found的解决方法

macOS12.6.6 M1安装wxPython失败&#xff1a; 报错如下&#xff1a; imagtiff.cpp:37:14: fatal error: tiff.h file not found解决办法&#xff1a; 下载源文件重新编译&#xff08;很快&#xff0c;5分钟全部搞定&#xff09;&#xff0c;分三步走&#xff1a; 第一步&…

Reading:Deep dive into the OnPush change detection strategy in Angular

原文连接&#xff1a;IndepthApp 今天深入阅读并总结Angualr中onPush更新策略。 1. 两种策略 & whats Lview&#xff1f; Angular 实现了两种策略来控制各个组件级别的更改检测行为。这些策略定义为Default和OnPush&#xff1a; 被定义为枚举&#xff1a; export enum…

IOC课程整理-3 Spring IoC 容器概述

1 Spring IoC依赖查找 延迟依赖查找主要用于获取 BeanFactory 后&#xff0c;不马上获取相关的 Bean&#xff0c;比如在 BeanFactoryPostProcessor 接口中获取 ConfigurableListableBeanFactory 时&#xff0c;不马上获取&#xff0c;降低 Bean 过早初始化的情况 2 Spring IoC…

redis缓存击穿,redisson分布式锁,redis逻辑过期

什么是缓存击穿&#xff1a; 缓存击穿是指在高并发环境下&#xff0c;某个热点数据的缓存过期&#xff0c;导致大量请求同时访问后端存储系统&#xff0c;引起系统性能下降和后端存储压力过大的现象。 解决方案&#xff1a; 1. redisson分布式锁 本质上是缓存重建的过程中&…

echarts的legend图例,要给图例中的不同文字设置不同颜色

可以用rich&#xff0c;先创建样式a&#xff0c;然后在formatter中用{a|文字}的形式使用&#xff0c;就能将文字使用a样式了

什么是全排列?(算法实现)

全排列是什么&#xff1f; 全排列是指将一组元素按照一定顺序进行排列的所有可能结果。以一组数字为例&#xff0c;比如[1, 2, 3]的全排列结果为&#xff1a;[1, 2, 3], [1, 3, 2], [2, 1, 3], [2, 3, 1], [3, 1, 2], [3, 2, 1]。 全排列有许多不同的计算方法&#xff0c;其中…

mybatis-plus正确使用姿势:依赖配置、Mapper扫描、多数据源、自动填充、逻辑删除。。。

一、前言 本文基于 springboot、maven、jdk1.8、mysql 开发&#xff0c;所以开始前我们需要准备好这套环境。 1.1 依赖准备 想要什么依赖版本的去 maven 仓库查看&#xff1a;https://mvnrepository.com/ 引入 mybatis-plus 依赖&#xff1a; <dependency><group…

【Linux】冯诺依曼体系结构以及初始操作系统

文章目录 冯诺依曼体系结构操作系统概念设计OS的目的定位如何理解管理 总结系统调用和库函数概念 冯诺依曼体系结构 我们常见的计算机&#xff0c;如笔记本。我们不常见的计算机&#xff0c;如服务器&#xff0c;大部分都遵守冯诺依曼体系。 截至目前&#xff0c;我们所认识…

黑豹程序员-架构师学习路线图-百科:jMeter并发测试计划

我们开发一个软件系统&#xff0c;为了保证代码的正确&#xff0c;我们需要测试。测试日常包括&#xff1a;单元测试、功能测试、集成测试、压力测试、回归测试。 Apache JMeter 是 Apache 组织基于 Java 开发的压力测试工具&#xff0c;用于对软件做压力测试。 JMeter 最初被…

JAVA反射机制及动态代理

反射机制 反射机制是什么 1、Java反射机制的核心是在程序运行时动态加载类并获取类的详细信息&#xff0c;从而操作类或对象的属性和方法。本质是JVM得到class对象之后&#xff0c; 再通过class对象进行反编译&#xff0c;从而获取对象的各种信息。 2、Java属于先编译再运行的…

Flutter 使用 GetX 中遇到的问题

创建了控制器&#xff0c;但是在别的页面中&#xff0c;无法引用控制器里面的某些变量 如下图&#xff1a;后来发现&#xff0c;是命名的问题&#xff0c; 如果是以 _ 下划线开头的变量&#xff0c;那么就无法被引用

(三)docker:Dockerfile构建容器运行jar包

目录结构以及准备的文件 ├── dockerfile │ ├── Dockerfile │ ├── application.properties │ ├── demo.jar │ └── jdk-17.0.9-linux-x64.tar.gz2.Dockerfile内容 FROM ubuntu:latest # JDK存放处 ENV JAVA_DIR/home # 拷贝本地jdk到容器home目录下…

京东数据分析:2023年9月京东洗地机行业品牌销售排行榜

鲸参谋监测的京东平台9月份洗地机市场销售数据已出炉&#xff01; 9月份&#xff0c;洗地机市场的销售额增长。根据鲸参谋电商数据分析平台的相关数据显示&#xff0c;9月京东平台上洗地机的销量为9.2万&#xff0c;销售额将近2.2亿&#xff0c;同比增长约9%。从价格上看&#…

如何使用htmltab库

htmltab是一个用于从HTML表格中提取数据的Python库。它可以将HTML表格转换为Pandas数据框&#xff0c;方便进行数据处理和分析。 要使用htmltab库&#xff0c;首先需要安装htmltab。可以使用pip命令来安装htmltab&#xff0c;命令如下&#xff1a; pip install htmltab 安装完…

[SHCTF 2023 校外赛道] pwn

有19道题这么多,不过基本是入门题,都是在骗新生,看这么容易快来PWN吧! week1 四则计算器 这里用危险函数gets读入有个溢出.而且PIE也没开,地址是固定的.而且有后门.直接溢出到ret写上后门即可. from pwn import *p remote(112.6.51.212, 31473) context(archamd64, log_lev…