【C++STL】“vector“容器的模拟实现

news2024/11/28 16:34:43

vector的模拟实现

  • 模拟实现
    • 成员变量
    • 构造函数
      • 无参构造函数
      • 初始化n个val的构造函数
      • 迭代器区间构造函数
    • 拷贝构造
    • 析构函数
    • begin()
    • end()
    • swap()
    • reserve()
    • resize()
    • capacity()
    • size()
    • 重载`[]`运算符
    • 重载=赋值运算符
    • insert()
    • erase()
    • push_back()
    • pop_back()
  • 完整代码
  • 动态二维数组的理解
  • 🍀小结🍀

🎉博客主页:小智_x0___0x_

🎉欢迎关注:👍点赞🙌收藏✍️留言

🎉系列专栏:C++初阶

🎉代码仓库:小智的代码仓库

模拟实现

成员变量

	iterator _start = nullptr;
	iterator _finish = nullptr;
	iterator _endofstorage = nullptr;

这里的iteratortypedef T* iterator;定义来的,T是模板参数。

  • _start是指向开始的指针变量。
  • _finish是指向最后一个元素的下一位的指针变量。
  • _endofstorage是指向当前最大容量处的指针变量。
template <class T>
class vector 
{
public:
	typedef T* iterator;
	typedef const T* const_iterator;
private:
	iterator _start = nullptr;
	iterator _finish = nullptr;
	iterator _endofstorage = nullptr;
};

在这里插入图片描述

构造函数

这里我们模拟实现三种构造函数:

无参构造函数

vector()
	;_start(nullptr)
	,_finish(nullptr)
	,_endofstorage(nullptr)
	{}

直接初始化三个指针变量。

初始化n个val的构造函数

vector(size_t n, const T& val = T())
	; _start(nullptr)
	, _finish(nullptr)
	, _endofstorage(nullptr)
{
	//开空间
	reserve(n);
	//初始化val
	for (size_t i = 0; i < capacity() ; i++)
	{
		_start[i] = val;
	}
	//也可以直接复用resize()
	//resize(n, val);
}

//构造函数重载防止构造时冲突
vector(int n, const T& val = T())
	; _start(nullptr)
	, _finish(nullptr)
	, _endofstorage(nullptr)
{
	//开空间
	reserve(n);
	//初始化val
	for (size_t i = 0; i < capacity(); i++)
	{
		_start[i] = val;
	}
	//也可以直接复用resize()
	//resize(n, val);
}

迭代器区间构造函数


template<class InputIterator>//模板参数
vector(InputIterator first, InputIterator last)
{
	while (first != last)
	{
		push_back(*first);//依次取出解引用并尾插
		++first;
	}
}

拷贝构造

vector(const vector<T>& v)
{
	//new一个capacity大小的空间
	_start = new T[v.capacity()];
	//逐个将值拷贝过去
	for (size_t i = 0; i < v.size(); i++)
	{
		_start[i] = v._start[i];
	}
	//重新写入_finish和_endofstorage的值
	_finish = _start + size();
	_endofstorage = _start + v.capacity();
}

析构函数

~vector()
{
	//如果_start为空的话就不做任何操作
	if (_start)
	{
		//释放_start申请的空间
		delete[] _start;
		//将三个成员变量全部置空
		_start = _finish = _endofstorage = nullptr;
	}
}

begin()

typedef T* iterator;
typedef const T* const_iterator;
//普通类型迭代器
iterator begin()
{
	return _start;
}
//const 迭代器
const_iterator begin() const
{
	return _start;
}

end()

typedef T* iterator;
typedef const T* const_iterator;
//普通迭代器
iterator end()
{
	return _finish;
}
//const 迭代器
const_iterator end() const
{
	return _finish;
}

swap()

void swap(vevtor<T>& v)
{
	//逐个交换每个成员变量
	std::swap(_start, v._start);
	std::swap(_finish, v._finish);
	std::swap(_endofstorage, v._endofstorage);
}

reserve()

void reserve(size_t n)
{
	//判断如果n大于capacity的话就扩容否则不做任何操作
	if (n > capacity())
	{
		//先保存size后面delete之后size()就失效了
		size_t sz = size();
		//new一个n大小的tmp变量
		iterator tmp = new T[n];
		if (_start)//_start不为空进行数据的拷贝
		{
			//for循环逐个拷贝数据
			for (size_t i = 0; i < sz; i++)
			{
				tmp[i] = _start[i];
			}
			//释放之前的数据
			delete[] _start;
		}
		//重新更新三个成员变量
		_start = tmp;
		_finish = tmp + sz;
		_endofstorage = _start + n;
	}
}

这里需要注意的是交换数据的时候不能使用memcpy(tmp, _start, sizeof(T) * sz);
以这段代码为例:

vector<string> v;
v.push_back("11111");
v.push_back("22222");
v.push_back("33333");
v.push_back("44444");

我们来画图理解使用memcpy拷贝的情况>
在这里插入图片描述

  1. memcpy是内存的二进制格式拷贝,将一段内存空间中内容原封不动的拷贝到另外一段内存空间中
  2. 如果拷贝的是内置类型的元素,memcpy既高效又不会出错,但如果拷贝的是自定义类型元素,并且 自定义类型元素中涉及到资源管理时,就会出错,因为memcpy的拷贝实际是浅拷贝。

我们再来画图理解一下逐个拷贝的情况>
在这里插入图片描述
【结论】: 如果对象中涉及到资源管理时,千万不能使用memcpy进行对象之间的拷贝,因为memcpy是 浅拷贝,否则可能会引起内存泄漏甚至程序崩溃。

resize()

void resize(size_t n, const T& val = T())
{
	//判断是否需要扩容
	if (n < size())
	{
		_finish = _start + n;//n<size()的时候更新_finish的值
	}
	else
	{
		//复用reserve()来调整容量
		reserve(n);
		while (_finish != _start + n)
		{
			//刚刚调整出的空间逐个写入val
			*_finish = val;
			++_finish;
		}
	}
}

capacity()

size_t capacity() const
{
	return _endofstorage - _start;//_endofstorage - _start就是它的最大容量
}

size()

size_t size() const
{
	return _finish - _start;//_finish - _start就是它的元素个数
}

重载[]运算符

普通[]重载:

T& operator[](size_t pos)
{
	//断言检查pos的位置是否合法
	assert(pos < size());
	return _start[pos];//直接return返回
}

const []重载:

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

重载=赋值运算符

vector<T>& operator =(vector<T> v)//形参接收实参会调用拷贝构造,构造出v
{
	//交换*this和v
	swap(v);
	return *this;//返回*this
}

v的作用域在这个函数内,出了函数就会自动析构,我们把*this和v的内容进行了交换,完了出了作用域v也会帮我们析构掉交换之后v的空间。

insert()

iterator insert(iterator pos, const T& x)
{
	assert(pos >= _start && pos <= _finish);//断言检查pos位置的有效性
	if (_finish == _endofstorage)//判断容量是否已经满了
	{
		size_t len = pos - _start;//记录pos前面位置的元素个数
		size_t newcapacity = capacity() == 0 ? 4 : capacity() * 2;
		reserve(newcapacity);
		// 解决pos迭代器失效问题
		pos = _start + len;
	}
	iterator end = _finish - 1;//记录最后一个元素的位置
	while (end >= pos)//逐个往后挪数据
	{
		*(end + 1) = *end;
		--end;
	}
	*pos = x;//给pos位置赋值x
	++_finish;//对应的_finish也要加1
	return pos;//返回pos位置
}

erase()

iterator erase(iterator pos)
{	
	assert(pos >= _start && pos < _finish);//断言检查pos位置的合法性
	iterator it = pos + 1;//保存pos下一个元素的地址
	while (it != _finish)
	{
		//逐个向前挪动数据
		*(it - 1) = *it;
		++it;
	}
	//对应的再将_dinish的值减1
	--_finish;
	return pos;//返回要删除的下一个位置即为新的pos
}

push_back()

void push_back(const T& x)
{
	//传统写法
	/*if (_finish == _endofstorage)//判断是否需要扩容
	{
		size_t newcapacity = capacity() == 0 ? 4 : capacity() * 2;
		reserve(newcapacity);
	}
	//给_finish赋值x
	*_finish = x;
	//将_finish加1
	++_finish;*/
	
	//复用insert()给end位置插入x
	insert(end(), x);
}

pop_back()

void pop_back()
{
	//复用erase()先是end()位置减1到最后一个元素的位置
	erase(--end());
}

完整代码

#pragma once
#include<iostream>
#include<assert.h>
using namespace std;

namespace CSDNYYDS {
	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;
		}

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

		vector()
		{}

		vector(size_t n, const T& val = T())
			; _start(nullptr)
			, _finish(nullptr)
			, _endofstorage(nullptr)
		{
			//开空间
			reserve(n);
			//初始化val
			for (size_t i = 0; i < capacity() ; i++)
			{
				_start[i] = val;
			}
			//也可以直接复用resize()
			//resize(n, val);
		}

		vector(int n, const T& val = T())
			; _start(nullptr)
			, _finish(nullptr)
			, _endofstorage(nullptr)
		{
			//开空间
			reserve(n);
			//初始化val
			for (size_t i = 0; i < capacity(); i++)
			{
				_start[i] = val;
			}
			//也可以直接复用resize()
			//resize(n, val);
		}
		//迭代器区间
		template<class InputIterator>
		vector(InputIterator first, InputIterator last)
		{
			while (first != last)
			{
				push_back(*first);
				++first;
			}
		}
		vector(const vector<T>& v)
		{
			_start = new T[v.capacity()];
			for (size_t i = 0; i < v.size(); i++)
			{
				_start[i] = v._start[i];
			}
			_finish = _start + size();
			_endofstorage = _start + v.capacity();
		}
		~vector()
		{
			if (_start)
			{
				delete[] _start;
				_start = _finish = _endofstorage = nullptr;
			}
		}
		void swap(vevtor<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;
		}
		void reserve(size_t n)
		{
			if (n > capacity())
			{
				size_t sz = size();
				iterator tmp = new T[n];
				if (_start)
				{
					for (size_t i = 0; i < sz; i++)
					{
						tmp[i] = _start[i];
					}
					delete[] _start;
				}
				_start = tmp;
				_finish = tmp + sz;
				_endofstorage = _start + n;
			}
		}

		void resize(size_t n,const T& val=T())
		{
			if (n < size())
			{
				_finsh = _start + n;
			}
			else
			{
				reserve(n);

				while (_finish!=_start+n)
				{
					*_finish = val;
					++_finish;
				}
			}
		}

		void push_back(const T& x)
		{
			insert(end(), x);
		}

		iterator insert(iterator pos, const T& x)
		{
			assert(pos >= _start && pos <= _finish);
			if (_finish == _endofstorage)
			{
				size_t len = pos - _start;

				size_t newcapacity = capacity() == 0 ? 4 : capacity() * 2;
				reserve(newcapacity);

				// 解决pos迭代器失效问题
				pos = _start + len;
			}
			iterator end = _finish - 1;
			while (end >= pos)
			{
				*(end + 1) = *end;
				--end;
			}
			*pos = x;
			++_finish;

			return pos;
		}

		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(--end());
		}

		

		T& operator[](size_t pos)
		{
			assert(pos < size());

			return _start[pos];
		}

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

			return _start[pos];
		}


	private:
		iterator _start = nullptr;
		iterator _finish = nullptr;
		iterator _endofstorage = nullptr;
	};
};

动态二维数组的理解

在这里插入图片描述

🍀小结🍀

今天我们认识了C++STL“vector“容器的模拟实现相信大家看完有一定的收获。
种一棵树的最好时间是十年前,其次是现在! 把握好当下,合理利用时间努力奋斗,相信大家一定会实现自己的目标!加油!创作不易,辛苦各位小伙伴们动动小手,三连一波💕💕~~~,本文中也有不足之处,欢迎各位随时私信点评指正!
本节课的代码已上传gitee仓库

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

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

相关文章

自学网络安全(黑客)为什么火了?

网安专业从始至终都是需要学习的&#xff0c;大学是无法培养出合格的网安人才的。这就是为啥每年网安专业毕业生并不少&#xff0c;而真正从事网安岗位的人&#xff0c;寥寥无几的根本原因。 如果将来打算从事网安岗位&#xff0c;那么不断学习是你唯一的途径。 网络安全为什…

应对 618、双十一等大促期间的高负载,API 性能测试应该怎么做?负载测试、基线测试、冒烟测试、浸泡测试、峰值测试和尖峰测试详解

随着应用程序和服务交付速度的不断提高&#xff0c;在按时交付应用程序的竞赛中&#xff0c;性能测试往往会退居其次。但是&#xff0c;在节假日期间&#xff0c;购物额都会大幅增长。在这种一年中的成败时刻&#xff0c;公司是无法接受他们的应用程序在高负载下变得不可靠的。…

第四章 云原生架构之Kubernetes基础知识

1、K8S整体架构 1.1、概述 ​ Kubernetes是一个可移植、可扩展的开源平台&#xff0c;用于管理容器化的工作负载和服务&#xff0c;简称 K8S。K8S的本质是一组服务器集群&#xff0c;可以在对应服务器集群的每个节点上运行程序&#xff0c;来对节点中的容器进行管理。类似Mas…

Pytest+Jenkins+Allure的接口自动化测试

目录 生成Allure 两种形式 一 项目内直接生成不依赖Jenkins 1.先安装好allure 将allure\bin配置到环境变量中 cmd 命令行输入&#xff1a;allure 校验是否安装成功 2. 将json文件生成html文件 执行 allure generate report/ -o report/html其中的report/ 为生成的json路径&a…

OCPM和CPM有什么区别?

CPM和OCPM这两种收费模式的对比 Cpm&#xff1a;表示千次展示费用&#xff0c;是数据指标&#xff0c;也是一种出价方式。代表展现一千次的消费&#xff0c;也就是你展现1000次要给媒体多少钱 例如某企业广告曝光量是50万&#xff0c;总广告价格为10000元&#xff0c;那么千人…

matplotlib 笔记:marker 款式

1 ec 边缘颜色 marker 边缘的颜色 import numpy as np import matplotlib.pyplot as pltxnp.linspace(0,10) ynp.sin(x)1.5 plt.figure(figsize(10,10)) plt.scatter(x,y,ecC9) plt.show() 2 fc 填充颜色 face color 填充颜色 3 lw 边缘宽度 4 s 点的大小 5 marker 点款式 i…

设计模式 ~ 工厂模式

工厂模式 工厂模式是一种设计模式&#xff0c;指在通过使用工厂函数或构造函数来创建对象&#xff1b; 它提供了一种灵活的方式来创建对象实例&#xff0c;而无需直接调用构造函数或使用new关键字&#xff1b; 可以分类&#xff0c;解耦&#xff1b; 可以扩展多个类&#xff0…

Baidu——基于大模型的优质Prompt开发课-写代码

软件开发产业趋势与技术革新 大模型驱动的软件开发 代码辅助开发模型 实际操作 你是一名非常专业的产品经理&#xff0c;请问如果我要做一个图片字符画的工具的调研&#xff0c;需要哪些步骤 你是一名编程大牛&#xff0c;目前我想做一个图像字符画的工具&#xff0c;这个工具要…

前缀、中缀、后缀表达式及简易运算实现总结

title: 前缀、中缀、后缀表达式及简易运算实现总结 date: 2023-06-30 10:25:50 tags: 表达式 categories:开发知识及其他 cover: https://cover.png feature: false 1. 概念 1.1 什么是前缀、中缀、后缀表达式&#xff1f; 前缀表达式&#xff1a;又称波兰式&#xff08;Pol…

代码随想录day5 | 242.有效的字母异位词 349. 两个数组的交集 202.快乐数

文章目录 一、有效的字母异位词二、两个数组的交集三、快乐数 一、有效的字母异位词 242.有效的字母异位词 代码随想录知识点 哈西法可以选取的三种数据结构&#xff1a; 数组setmap class Solution { public:bool isAnagram(string s, string t){int hash[26] {0};// 1f…

CMS系统访问权限限制

创建一些全局的通用方法 const USER_KEY "USER_KEY" const TOKEN_KEY "JWT_TOKEN_KEY"class Auth {constructor() {this.token nullthis.user nullthis.token localStorage.getItem(TOKEN_KEY)const userJson localStorage.getItem(USER_KEY)if (use…

Redis连接报错:ERR Client sent AUTH, but no password is set

如果在redis.windows.conf或者redis.conf&#xff08;我的是这个配置文件&#xff09; 文件夹中设置了密码&#xff0c;但是会报错 ERR Client sent AUTH, but no password is set 用记事本打开redis.windows.conf或者redis.conf &#xff08;我的是这个配置文件&#xff09;…

一次性讲清楚常考面试题:进程和线程的区别

进程是程序的一次动态执行&#xff0c;它对应着从代码加载&#xff0c;执行至执行完毕的一个完整的过程&#xff0c;是一个动态的实体&#xff0c;它有自己的生命周期。它因创建而产生&#xff0c;因调度而运行&#xff0c;因等待资源或事件而被处于等待状态&#xff0c;因完成…

今天实习第三天,vue(vue-cli部分,webpack部分,vue-router部分,elementUI部分)

01.创建第一个vue-cli。这里用的是node.js。早上的时候&#xff0c;就需要把node.js安装上去 02.node.js安装 第一步.去官网下载node.js https://nodejs.org/en 第二步.运行官网下载的node.js的msi文件&#xff08;记住所有的node.js文件的安装包都是msi文件的形式&#xff0…

(学习笔记-TCP连接建立)IP层会分片,为什么TCP层还需要MSS呢?

前提知识&#xff1a; 网络层最常用的是IP协议&#xff0c;IP协议会将传输层的报文作为数据部分&#xff0c;再加上IP包头组装成IP报文&#xff0c;如果IP报文大小超过了MTU(1500字节)就会再次分片&#xff0c;得到一个即将发送到网络的IP报文 MTU和MSS: MTU&#xff1a;一个网…

如何在 Excel 中快速生成随机密码?

有时&#xff0c;我们可能想创建随机密码来保护某些重要内容。 但是&#xff0c;您有什么技巧可以在Excel中快速生成随机密码&#xff1f; 在这里&#xff0c;我有一些可以在Excel工作表中处理的方法。 用公式生成随机密码 使用插入随机数据生成随机密码​编辑 用公式生成随机…

普通人的姓名可以注册为商标吗?

商标是商品的生产者、经营者在其生产、制造、加工或者经销的商品上或者服务使用的标志&#xff0c;用于区别商品或服务来源。商标由文字、图形、字母、数字、三维标志、颜色组合和声音等组合而成&#xff0c;以姓名注册商标属于文字商标&#xff0c;因此&#xff0c;个人的名字…

第十二章:MULTI-SCALE CONTEXT AGGREGATION BY DILATED CONVOLUTIONS——通过膨胀卷积的多尺度上下文聚合

0.摘要 目前用于语义分割的先进模型是基于最初设计用于图像分类的卷积网络的改进。然而&#xff0c;像语义分割这样的密集预测问题在结构上与图像分类不同。在这项工作中&#xff0c;我们开发了一个专门为密集预测设计的新的卷积网络模块。所提出的模块使用膨胀卷积来系统地聚合…

QQ号码3个月未登陆真的要回收?

7月17日消息&#xff0c;微信号长期未使用会被回收的消息引起热议。 腾讯微信团队微博发文称&#xff1a;为保障用户的微信账号安全&#xff0c;注册后不活跃&#xff0c;长期未登录&#xff0c;并且没有零钱的微信账号&#xff0c;会被系统注销&#xff0c;无法使用。 不过也有…

B. The BOSS Can Count Pairs

Problem - 1830B - Codeforces 思路&#xff1a;因为ai*ajbibj&#xff0c;bibj<2*n&#xff0c;那么会有ai*aj<2*n&#xff0c;那么会有min(ai,aj)<sqrt(2*n)&#xff0c;我们能够发现我们只要枚举ai&#xff08;假设ai<aj&#xff09;那么只要在这种情况下求得所…