[C++随笔录] vector模拟实现

2023-09-22 15:46:32

基本结构

// 可以是不同类型, 用类模板
template <class T>
class vector
{
public:
	// 源码里面成员变量的类型用的是迭代器,
	// 所以, 先定义迭代器类型
	typedef T* iterator;
	typedef const T* const_iterator;
	
private:
	iterator _start = nullptr; // 相当于string类中的 _str
	iterator _finish = nullptr; // 相当于string类中的 _size
	iterator _endofstorage = nullptr; // 相当于string类中的 _capacity
}
  1. 成员变量先给缺省值, 便于后面的构造函数 和 拷贝构造函数
  2. 迭代器是 T* , 跟string类中 迭代器是 char* 是一样的道理

天选之子

构造

  1. 默认构造函数
vector()
	:_start(nullptr)
	, _finish(nullptr)
	, _endofstorage(nullptr)
{

}

由于我们给成员变量都给了缺省值, 那么👇👇👇

vector()
{
}

  1. 开空间 + 初始化
    开空间 + 初始化 也是 resize 干的事情, 那么我们就可以直接复用
vector(int n, const T& val = T()) // 缺省值给T的默认构造出来的对象
{
	resize(n, val);
}
  1. 迭代器区间初始化

从上一篇文章得出: 同类型, 不同类型, 数组的区间都可以进行初始化. 迭代器多样化 ⇒ 套用模版
⇒ 进而我们得出: 在模版中可以套用模版

template <class InputIterator>
vector(InputIterator first, InputIterator last)
{
	int n = last - first;
	resize(n);

	int i = 0;
	while (first != last)
	{
		_start[i++] = *first;
		first++;
	}

}

拷贝构造

vector(const vector<T>& tem)
{
	// 找一块新空间 -- 外部深拷贝
	_start = new T[tem.capacity()];
	
	// memcpy(_start, tem._start, tem.capacity); -- 内部浅拷贝, 是错误的
	for (int i = 0; i < tem.size(); i++) // 内部深拷贝
	{
		_start[i] = tem[i];
	}
	
	// 更新size 和 capacity
	_finish = _start + tem.size();
	_endofstorage = _start + tem.capacity();

}
  • 不能使用memcpy来进行拷贝数据的原因 && 外部和内部的深拷贝图示

析构

~vector()
{
	// 释放空间
	delete[] _start;

	// 置空
	_start = _finish = _endofstorage = nullptr;
}

operator=

// 现代写法 -- 传值传参, 巧借拷贝构造
T& operator=(const T tem)
{
	swap(tem);

	return *this;
}

空间

reserve

void reserve(size_t n)
{
	assert(n > 0);

	if (n > capacity())
	{
		size_t sz = size();  // 应该先保存一份sz <== _start会发生变化
		T* tem = new T[n];
		
		// 拷贝数据
		// memcpy(tem._start, _start, n); // 内部浅拷贝
		for (int i = 0; i < size(); i++)
		{
			tem[i] = _start[i]; //调用了T的赋值, 从而实现深拷贝
		}

		// 更新_start
		delete[] _start;
		_start = tem;

		// 更新size 和 capacity
		_finish = _start + sz;
		_endofstorage = _start + n;
	}

}

resize

void resize(size_t n, const T& val = T())
{
	assert(n > 0);
	
	// 缩
	if (size() > n)
	{
		_finish = _start + n;
	}
	// 扩
	else
	{
		reserve(n); // 先开n个空间

		// 从_finish位置开始初始化
		for (int i = size(); i < size() + n; i++)
		{
			_start[i] = val;
		}

		// 改变_finish
		_finish = _finish + n;

	}
}

size && capacity

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

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

insert

void insert(iterator pos, const T& val = T())
{
	assert(pos >= _start && pos <= _finish);

	size_t len = pos - _start; // 在扩容前, 先保存一下pos的相对位置, 以免异地扩容, _start发生变化, 导致pos迭代器失效

	// 是否扩容
	if (_finish == _endofstorage)
	{
		// 考虑到首次插入
		size_t newcapacity = capacity() == 0 ? 4 : capacity() * 2;
		reserve(newcapacity);

		pos = _start + len; // 扩容后, 更新pos
	}

	// 往后挪动数据
	iterator end = _finish - 1; 
	while (end >= pos)
	{
		*(end + 1) = *end;
		end--;
	}

	// 插入
	*pos = val;
	_finish = _finish + 1;

}

push_back

void push_back(const T& val = T())
{
	 是否扩容
	//if (_finish == _endofstorage)
	//{
	//	size_t newcapacity = capacity() == 0 ? 4 : capacity() * 2;
	//	reserve(newcapacity);

	//}

	//*_finish = val;
	//++_finish;
	
	// 复用insert
	insert(_finish, val);
}

erase

iterator erase(iterator pos)
{
	assert(pos >= _start && pos < _finish);
	
	// 往前挪动数据
	iterator it = pos + 1 ;
	while (it != _finish)
	{
		*(it - 1) = *it;
		it++;
	}
	
	// 更新size
	--_finish;

	return pos;
}

pop_back

void pop_back()
{
	// 复用erase, 传参_finish - 1
	erase(--end());

}

查 && 改

swap

void swap(vector<T>& tem)
{
	std::swap(_start, tem._start);
	std::swap(_finish, tem._finish);
	std::swap(_endofstorage, tem._endofstorage);

}

operator[]


T& operator[](size_t n)
{
	return _start[n];
}

const T& operator[](size_t n)const 
{
	return _start[n];
}

源码

#pragma once

#include<assert.h>
#include<iostream>

namespace muyu
{
	template <class 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(int n, const T& val = T()) // 缺省值给	T的默认构造出来的对象
		{
			resize(n, val);
		}

		template <class InputIterator>
		vector(InputIterator first, InputIterator last)
		{
			int n = last - first;
			resize(n);

			int i = 0;
			while (first != last)
			{
				_start[i++] = *first;
				first++;
			}

		}

		vector(const vector<T>& tem)
		{
			// 找一块新空间 -- 外部深拷贝
			_start = new T[tem.capacity()];

			// memcpy(_start, tem._start, tem.capacity); -- 内部浅拷贝, 是错误的
			for (int i = 0; i < tem.size(); i++) // 内部深拷贝
			{
				_start[i] = tem[i];
			}

			// 更新size 和 capacity
			_finish = _start + tem.size();
			_endofstorage = _start + tem.capacity();

		}

		~vector()
		{
			// 释放空间
			delete[] _start;

			// 置空
			_start = _finish = _endofstorage = nullptr;
		}

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

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

		void reserve(size_t n)
		{
			assert(n > 0);

			if (n > capacity())
			{
				size_t sz = size();  // 应该先保存一份sz <== _start会发生变化
				T* tem = new T[n];

				// 拷贝数据
				// memcpy(tem._start, _start, n); // 内部浅拷贝
				for (int i = 0; i < size(); i++)
				{
					tem[i] = _start[i]; //调用了T的赋值, 从而实现深拷贝
				}

				// 更新_start
				delete[] _start;
				_start = tem;

				// 更新size 和 capacity
				_finish = _start + sz;
				_endofstorage = _start + n;
			}

		}

		void resize(size_t n, const T& val = T())
		{
			assert(n > 0);

			if (size() > n)
			{
				_finish = _start + n;
			}
			else
			{
				reserve(n); // 先开n个空间

				// 从_finish位置开始初始化
				for (int i = size(); i < size() + n; i++)
				{
					_start[i] = val;
				}

				// 改变_finish
				_finish = _finish + n;

			}
		}

		void push_back(const T& val = T())
		{
			 是否扩容
			//if (_finish == _endofstorage)
			//{
			//	size_t newcapacity = capacity() == 0 ? 4 : capacity() * 2;
			//	reserve(newcapacity);

			//}

			//*_finish = val;
			//++_finish;

			insert(_finish, val);
		}

		void insert(iterator pos, const T& val = T())
		{
			assert(pos >= _start && pos <= _finish);

			size_t len = pos - _start; // 在扩容前, 先保存一下pos的相对位置, 以免异地扩容, _start发生变化, 导致pos迭代器失效

			// 是否扩容
			if (_finish == _endofstorage)
			{
				size_t newcapacity = capacity() == 0 ? 4 : capacity() * 2;
				reserve(newcapacity);

				pos = _start + len; // 扩容后, 更新pos
			}

			// 往后挪动数据
			iterator end = _finish - 1;
			while (end >= pos)
			{
				*(end + 1) = *end;
				end--;
			}

			// 插入
			*pos = val;
			_finish = _finish + 1;

		}

		T& operator[](size_t n)
		{
			return _start[n];
		}

		const T& operator[](size_t n)const 
		{
			return _start[n];
		}

		void swap(vector<T>& tem)
		{
			std::swap(_start, tem._start);
			std::swap(_finish, tem._finish);
			std::swap(_endofstorage, tem._endofstorage);

		}

		iterator erase(iterator pos)
		{
			assert(pos >= _start && pos < _finish);

			iterator it = pos + 1 ;
			while (it != _finish)
			{
				*(it - 1) = *it;
				it++;
			}

			--_finish;

			return pos;
		}
		
		void pop_back()
		{
			// 复用erase, 传参_finish - 1
			erase(--end());
		
		}

		// 现代写法 -- 传值传参, 巧借拷贝构造
		T& operator=(const T tem)
		{
			swap(tem);

			return *this;
		}

	private:
		iterator _start = nullptr; // 相当于string类中的 _str
		iterator _finish = nullptr; // 相当于string类中的 _size
		iterator _endofstorage = nullptr; // 相当于string类中的 _capacity

	};
}


更多推荐

vue3硅谷甄选01 | 使用vite创建vue3项目及项目的配置 环境准备 ESLint配置 prettier配置 husky配置 项目集成

文章目录使用vite创建vue3项目及项目的配置1.环境准备2.项目配置ESLint校验代码工具配置-js代码检测工具1.安装ESLint到开发环境devDependencies2.生成配置文件:`.eslint.cjs`**3.安装vue3环境代码校验插件**4.修改.eslintrc.cjs配置文件5.生成ESLi

接口自动化测试框架搭建全部过程

思想:1、基本目录的搭建report:静态输出目录(报告或者日志)data:静态输入目录(可以存放Excel数据,被读取的一些数据)utils:实用方法层(这里存放的是项目的公共方法,一般拿到别的项目可以直接使用,列如:读取Excel中的数据,连接数据库,)apis:接口请求层(这里封装的方法一般都是和项目有关系,列如

MySQL 权限分配

有时候,您需要查看某个用户被授予的权限以便复核。MySQL允许您使用SHOWGRANTS语句来显示分配给用户帐户或角色的权限。MySQLSHOWGRANTS语句介绍以下是SHOWGRANTS语句的基本语法:SHOWGRANTS[FOR{user|role}[USINGrole[,role]...]]在这个语法中:首先,

记录一次DLL分析实战

记录一次DLL分析实战1.VT查看分析报告2.判断文件是否加壳3.查看导入函数4.查看是否有任何其他文件或基于主机的迹象5.使用工具IDAPro进行字符串分析1.VT查看分析报告virustotal全绿,没有报毒:可以看到这个dll是32位的:下面可以看它调用的其他dll:以及它对外提供的函数接口:其中RunCmd很可

Redis简介

1.Nosql作用:应对基于海量用户和海量数据前提下的数据处理问题。​常见Nosql数据库:​RedismemcacheHBaseMongoDB​特征:可扩容,可伸缩,大数据量下高性能,灵活的数据模型,高可用2.Redis特征:1.数据间没有必然的关联关系2.内部采用单线程机制进行工作3.高性能。官方提供测试数据,50

OceanBase杨传辉传递亚运火炬:国产数据库为“智能亚运”提供稳稳支持

9月14日,亚运火炬传递到了浙江台州,OceanBase的CTO杨传辉作为火炬手交接了第89棒火炬。2010年,杨传辉作为创始成员之一参与自研原生分布式数据库OceanBase。十年磨一剑,国产数据库OceanBase交出了一张优秀的成绩单:连续10年稳定支撑双11,承受住了世界级的流量洪峰和稳定性考验;刷新过TPC-

go学习-GMP模型

GMP好理解还是GPM好理解?按照上述图,从上往下,GPM更适合理解GMP模型:Go语言运行时系统中的Goroutine、用于管理Goroutine调度的GoScheduler(P)、机器可用的逻辑处理器数量(M)。理解GPMG每个Goroutine是一个轻量级“线程”,称之为“协程”,可由Go运行时系统并发执行G与P

2023 Google 开发者大会:将大型语言模型部署到你的手机

在2022年末,不到半年时间,各家大语言模型的发展如雨后春笋,截至2023年9月,全球总共有接近100个大语言模型,可谓是百花齐放显而易见,大语言模型凭借出色的AI对话能力,已经逐渐深入各个行业2023Google开发者大会带来了AI专题,Google技术推广工程师魏巍提出“将大语言模型部署到个人终端”,关于这点,在外

[NLP] LLM---<训练中文LLama2(三)>对LLama2进行中文预料预训练

预训练预训练部分可以为两个阶段:第一阶段:冻结transformer参数,仅训练embedding,在尽量不干扰原模型的情况下适配新增的中文词向量。第二阶段:使用LoRA技术,为模型添加LoRA权重(adapter),训练embedding的同时也更新LoRA参数。第一阶段预训练由于第一阶段预训练会冻结transfor

Java基于SpringBoot的校园交友网站的设计与实现

博主介绍:✌程序员徐师兄、7年大厂程序员经历。全网粉丝30W+,Csdn博客专家、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌文章目录一、效果演示二、前言介绍三、主要技术四、系统设计(部分)4.1、主要功能模块设计4.2、系统登录流程设计五、运行截图5.1、系统功能模块5.1.

Linux高性能服务器编程 学习笔记 第六章 高级IO函数

pipe函数用于创建一个管道,以实现进程间通信:fd参数是一个包含两个int的数组。该函数成功时返回0,并将一对打开的文件描述符填入其参数指向的数组,如果失败,则返回-1并设置errno。pipe函数创建的这两个文件描述符fd[0]和fd[1]分别构成管道的两端,往fd[1]写入的数组可以从fd[0]读出,并且fd[0

热文推荐