C++初阶 Vector模拟实现

news2025/7/21 4:31:51

q. > 作者:@小萌新

专栏:@C++初阶
作者简介:大二学生 希望能和大家一起进步
本篇博客介绍:本篇博客会模拟Vector实现
在这里插入图片描述

学习目标

  1. 模拟默认函数实现
  2. 模拟迭代器实现
  3. 模拟容器大小相关函数
  4. 模拟修改内容相关函数
  5. 模拟访问容器相关函数

我们先来看看整体的模板是什么样子的

vector是一个经典的类模板 所以我们这里使用泛型编程

namespace shy
{
	template<class T> // 使用模板
	class vector
	{
	public:
		typedef T* itreator; // 使用迭代器
		typedef const T* const_iterator; // const 迭代器

	private:
		iterator _start; // 指向容器的头
		iterator _finish; // 指向有效数据的尾
		iterator _endofstorage; // 指向容器的尾
	};
}

模拟默认函数的实现

构造函数

无参构造

顾名思义就是不传参数构造 那我们这里就给几个默认值就好

		vector<T>()                      // 无参 默认初始化 
			:_start(nullptr),
			_finish(nullptr),
			_endofstorage(nullptr)
		{}

我们来看看运行效果

在这里插入图片描述
可以运行 不会报错

迭代器构造

除了无参构造之外 vector类还支持迭代器构造

(这两个要实现后面的函数之后复用才比较简便 所以我们后面再回来实现这个函数)

		template<class InputIterator> //模板函数
		vector(InputIterator first, InputIterator last)
			:_start(nullptr)
			, _finish(nullptr)
			, _endofstorage(nullptr)
		{
			//将迭代器区间在[first,last)的数据一个个尾插到容器当中
			while (first != last)
			{
				push_back(*first);
				first++;
			}
		}

指定构造

此外vetor构造函数还支持指定大小构造相同的值 也是一样 我们后面先实现其他函数之后再来实现它们

所以说我们这里要输入两个参数 一个是大小 一个是值

那么思路就很清晰了 插入n个这个值就好啦

//构造函数3
vector(size_t n, const T& val)
	:_start(nullptr)
	, _finish(nullptr)
	, _endofstorage(nullptr)
{
	reserve(n); //调用reserve函数将容器容量设置为n
	for (size_t i = 0; i < n; i++) //尾插n个值为val的数据到容器当中
	{
		push_back(val);
	}
}

析构函数

释放空间 全部指针(迭代器)置空即可 代码表示如下


		~vector()
		{
			delete[] _start;
			_start = nullptr; //_start置空
			_finish = nullptr; //_finish置空
			_endofstorage = nullptr; //_endofstorage置空
		}

拷贝构造

思路如下

我们首先将空间扩大至要拷贝的空间

然后一个个的插入数据即可

代码表示如下

//现代写法
vector(const vector<T>& v)
	:_start(nullptr)
	, _finish(nullptr)
	, _endofstorage(nullptr)
{
	reserve(v.capacity()); //调用reserve函数将容器容量设置为与v相同
	for (auto e : v) //将容器v当中的数据一个个尾插过来
	{
		push_back(e);
	}
}

赋值运算符重载

这里也很简单

我们使用传值拷贝 然后交换它们的三个指针就可以

//现代写法
vector<T>& operator=(vector<T> v) //编译器接收右值的时候自动调用其拷贝构造函数
{
	swap(v); //交换这两个对象
	return *this; //支持连续赋值
}

容量大小相关函数

capacity

这个函数的意思很明显 就是返回容量有多少就可以

代码也很简单


		int capacity()
		{
			return _endofstorage - _start; // 计算容量大小
		}

size

返回有效元素的个数 代码也是一样的


		int size()
		{
			return _finish - _start; // 计算有效元素个数
		}

empty

判断是否为空 这里只要判断下首和尾是否相同就可以了

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

reserve

reserve的使用规则如下

  1. 如果reserve的大小小于本来容器的容量 那就什么都不做
  2. 如果reserve的大小大于本来容器的容量 那就扩容至给予的大小

代码表示和效果图如下

	void reserve(int n)
		{
			if (n > this->capacity()) // 这里写this指向也可以
			{
				size_t sz = size(); //记录一下原本的大小是多少
				T* tmp = new T[n]; // 开辟出来这么大的空间
				// 这里开始将原本空间的所有数据拷贝进去
				if (_start)
				{
					int i = 0;
					while (i<sz)
					{
						tmp[i] = *(_start + i);
						i++;
					}
					// 走到这里全部赋值完毕
				}
				delete[] _start; // 将原本的容器删除

				_start = tmp;
				_finish = _start + sz;
				_endofstorage = _start + n;
				// 这里它的三个指标也要改变
			}
		}

这里我们有两个注意点

一 我们必须提前保存size的值

这是为什么呢? 还记不记我们要在什么时候使用sz

是在start被赋值tmp的时候吧 那么这个时候start的位置是不是改变了

size的值是不是就失真了啊 (size = finish - start (原本的))

所以说我们要提前保存size的值

二 我们这里必须使用传值赋值不能使用memcpy

为什么呢?

如果是自定义类型的话 是不是传值还是memcpy没有任何区别

但是如果是自定义类型呢?

假如我们使用memcpy进行复制的话 是不是就是连指针指向的位置都复制过来了啊

那么当我们delete原来空间的时候是不是全部会自动调用析构函数然后不就直接g了

开新空间开了个寂寞

所以说这个时候用传值拷贝就可以了

resize

resize的使用规则是这样子的

首先它有两个参数 一个是我们要resize的大小 一个是默认初始化的值

  1. 假设我们要resize的大小小于目前的有效元素 我们将n赋值给_finish
  2. 假设我们要resize的大小大于目前的有效元素并且小于有效容量时 我们将_finish到n这一段赋值给我们给的值
  3. 假设我们要resize的大小大于目前的有效元素并且大于有效容量时 扩容有效容量大小到n 之后重复2的操作
		void resize(int n ,const T& val = T())
		{
			assert(n >= 0);
			// 首先处理最简单的情况 也就是resize小于当前有效元素 
			if (n < size())
			{
				_finish = n;
			}

			if (n > size())
			{
				reserve(n); // 这里不用管其他的直接用就行 因为容量小于caoacity是什么都不会发生的

				while (_finish < _start + n)// 注意不是finish大于n 而是要大于start+n 
				{
					*_finish = val;
					_finish++;
				}
			}
		}

修改内容相关函数

push_back

见名知义 往后插入一个元素 和前面一样 我们需要判断是否需要扩容

这个时候我们的引用传参的左右就来了 万一我们要传递的参数是一个非常大大的数组呢?

如果传值传递是不是效率就特别低了

代码表示如下


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

			_finish = val; // 赋值
			_finish++; // 指针++
		}

pop_bakc

尾删 这个操作就更简单了 只不过有一个小注意点要判断下是否为空

	void pop_back()
		{
			assert(!empty());
			_finish--;
		}

insert

insert函数可以在指定的pos位置插入一个数据

我们需要给两个参数 一个是我们要插入位置的迭代器 一个是要插入的数据

代码表示如下

		void insert(iterator pos, const T& val)
		{


	    	// 首先要判断是否需要扩容
			if (_endofstorage == _finish)
			{
				int len = pos - _start;
				reserve(capacity() == 0 ? 4 : 2 * capacity());
				pos = _start + len; // 为什么这么操作呢? 因为扩容之后start的位置有可能改变
			}

			iterator end = _finish; 
			// 将前面一个位置移动到后面一个位置 pos位置不移动
			while (pos < end)
			{
				*(end) = *(end - 1);
				end--;
			}
			*pos = val;
			_finish++;
		}

earse

这里可以指定位置函数 还是一样记得要判断是否为空

还有一点特殊点就是它会返回被它删除之后的下一位迭代器!

代码表示如下


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

迭代器相关函数

这里返回的都很简单 我们直接写就好了

		iterator begin()
		{
			return _start;
		}

		iterator end()
		{
			return _finish;
		}

		const_iterator begin()
		{
			return _start;
		}


		const_iterator end()
		{
			return _finish;
		}

访问下标有关

Vector还支持下标访问

所以说我们这里要重载下下标访问运算符

代码表示如下

		T& operator[](size_t i)
		{
			assert(i < size()); //检测下标的合法性

			return _start[i]; //返回对应数据
		}
		const T& operator[](size_t i)const
		{
			assert(i < size()); //检测下标的合法性

			return _start[i]; //返回对应数据
		}

总结

在这里插入图片描述

本篇博客主要介绍了vector的模拟实现
由于作者水平有限 错误在所难免 希望大佬看到之后可以及时指正
如果这篇文章帮助到了你 别忘记一键三连啊
阿尼亚 哇酷哇酷!

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

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

相关文章

xss挑战之旅11-19关

文章目录前言第11关&#xff1a;referer第12关&#xff1a;User-Agent第13关&#xff1a;cookie第14关&#xff1a;exif xss第15关&#xff1a;ng-include第16关第17关第18关第19关&#xff1a;flash xss前言 靶场&#xff1a;XSS挑战之旅 1-10关 11-20关 第11关&#xff1a;r…

『LeetCode|每日一题』---->颜色填充

目录 1.每日一句 2.作者简介 『LeetCode|每日一题』颜色填充 1.每日一题 2.解题思路 2.1 思路分析&#xff08;DFS&#xff09; 2.2 核心代码 2.3 完整代码 2.4 运行结果 1.每日一句 我的宇宙为你藏着无数个星球 2.作者简介 &#x1f3e1;个人主页&#xff1a;XiaoXiaoChe…

Git之路

文章目录指南介绍实战任务一&#xff1a;sb项目任务二&#xff1a;idea实战任务三&#xff1a;分支实战(待续)指南 如果你想在简历上写“会常用的Git的命令“&#xff0c;那么这篇文章值得你要看&#xff0c;那我们需要掌握什么呢&#xff1f;其实会简单的操作就行&#xff0c…

Matplotlib绘图-快速上手可视化工具

Matplotlib快速上手一、初识Matplotlib1.1 认识Matplotlib的图像结构1.2 绘制一个折线图二、给图像添加修饰2.1 自定义x的刻度2.2一图多线2.3一图绘制多个坐标系子图三、主流图形的绘制3.1绘制柱状图一、初识Matplotlib 是Python最常见的可视化工具之一 1.1 认识Matplotlib的…

csrf跨站请求伪造

文章目录csrf跨站请求伪造1、前戏2、csrf校验2.1、from表单如何符合校验2.2、ajax如何符合校验3、csrf相关装饰器FBVCBVcsrf跨站请求伪造 1、前戏 """ 钓鱼网站搭建一个跟正规网站一摸一样的界面&#xff08;中国银行&#xff09;用户进入到我们的网站&#x…

HyperLynx(三十)高速串行总线仿真(二)

高速串行总线仿真&#xff08;二&#xff09; 仿真实例 1.探索多层板中的PCI-E串行通道 2.设置叠层以减小损耗 3.分析通道的不同配置对损耗的影响 4.检测驱动端规范 5.检查接收器规范 6.通过仿真得出整个通道的驱动约束限制 1.探索多层板中的PCI-E串行通道 在本节练习中&…

人工智能学习:Microsoft COCO数据集读取(7)

Microsoft COCO&#xff08;Common Objects in Context&#xff09;是微软研发维护的一个大型的数据集。包含了30多万张图片和91个目标分类。可用于目标识别&#xff08;Object Detection&#xff09;、场景感知&#xff08;Penoptic Segmentation&#xff09;、语义分割&#…

【数据结构】——单链表

目录 1.链表 1.1 链表的概念及结构 1.2 链表的分类 1. 单向或者双向 2. 带头或者不带头 3. 循环或者非循环 1.3实现一个单链表&#xff08;无头单项非循环链表增删查改的实现&#xff09; 1.链表结构的创建 2.创建一个节点 3.创建一个链表 4.打印链表 5…

解读JVM级别本地缓存Caffeine青出于蓝的要诀 —— 缘何会更强、如何去上手

大家好&#xff0c;又见面了。 在前面的几篇文章中&#xff0c;我们一起聊了下本地缓存的动手实现、本地缓存相关的规范等&#xff0c;也聊了下Google的Guava Cache的相关原理与使用方式。比较心急的小伙伴已经坐不住了&#xff0c;提到本地缓存&#xff0c;怎么能不提一下“地…

软考 - 程序语言设计

程序设计语言基本概述 程序设计语言是为了书写计算机程序而人为设计的符号语言&#xff0c;用于对计算过程进行 描述、组织和推导。 低级语言&#xff1a;机器语言&#xff08;计算机硬件只能识别0和1的指令序列&#xff09;&#xff0c;汇编语言。 高级语言&#xff1a;功能…

从http请求过程分析为何不同业务的http请求都可以使用默认的缺省端口80,8080等

问题: http上传请求时url地址中一般无显示指定端口号&#xff0c;这时会使用默认的80端口&#xff1b;但是可能不止一个业务需要用到http请求&#xff0c;技术上web服务端那边肯定无法根据业务逻辑的数据格式去分别解析区分它们&#xff1b;因为业务是事先无法预知的&#xff…

【Spring Cloud实战】Consul服务注册与发现

个人博客上有很多干货&#xff0c;欢迎访问&#xff1a;https://javaxiaobear.gitee.io/ 1、简介 https://www.consul.io/docs/intro Consul is a service mesh solution providing a full featured control plane with service discovery, configuration, and segmentation f…

Flink-经典案例WordCount快速上手以及安装部署

2 Flink快速上手 2.1 批处理api 经典案例WordCount public class BatchWordCount {public static void main(String[] args) throws Exception {//1.创建一个执行环境ExecutionEnvironment env ExecutionEnvironment.getExecutionEnvironment();//2.从文件中读取数据//得到…

[附源码]java毕业设计基于Web留学管理系统

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

Linux下C++开发笔记--编译静态链接库和动态链接库

目录 1--前言 2--生成静态链接库 3--生成动态链接库 1--前言 承接上一篇Linux下C开发笔记&#xff08;g命令的使用笔记&#xff09;&#xff0c;依据教程记录学习笔记。 2--生成静态链接库 ①回顾项目结构&#xff1a; ​ ②汇编&#xff0c;生成swap.o文件 cd srcg sw…

基于simulink的牛鞭效应模型建模与仿真

欢迎订阅《FPGA学习入门100例教程》、《MATLAB学习入门100例教程》 目录 一、理论基础 二、核心程序 三、测试结果 一、理论基础 牛鞭效应&#xff0c;是经济学中的一个术语&#xff0c;它也被称为需求放大效应。牛鞭效应指的是当信息流从最终客户端传输到原始供应商时&…

9.行为建模(Behavioral modeling)

9.1行为模型概述 Verilog行为模型包含控制模拟和操纵先前描述的数据类型变量的过程语句。这些语句包含在程序中。每个过程都有一个与其关联的活动流。活动开始于initial和always语句。每个initial语句和每个always语句都会启动一个单独的活动流。所有活动流都是并发的&…

【机器学习】线性分类【上】广义线性模型

主要参考了B站UP主“shuhuai008”&#xff0c;包含自己的理解。 有任何的书写错误、排版错误、概念错误等&#xff0c;希望大家包含指正。 由于字数限制&#xff0c;分成两篇博客。 【机器学习】线性分类【上】广义线性模型 【机器学习】线性分类【下】经典线性分类算法 1. 线…

C语言实现线索化二叉树(先序、中序、后序)

》》如何用C语言构建一颗二叉树? 第一种方法: ThreadTree A = (ThreadTree)malloc(sizeof(ThreadNode));A->data = { A };A->ltag = 0;A->rtag = 0;A->lchild = NULL;A->rchild = NULL;ThreadTree B = (ThreadTree)malloc(sizeof(ThreadNode));B->data =…

【python自动化】使用关键字驱动实现appium自动化

在写app自动化用例时&#xff0c;尝试用了关键字驱动的框架 记录一下自己对关键字驱动的理解&#xff1a; 1 关键字驱动指将用例步骤的操作封装为关键字&#xff0c;比如定位元素、点击元素、获取元素属性值、断言&#xff0c;这些都是操作关键字 2 在excel中按照用例执行过程&…