【C++】vector的实现

news2024/9/28 19:25:49

模拟实现vector类

  • 前言
  • 一、迭代器
  • 二、重载 [ ]
  • 三、构造函数相关(重点)
    • (1)构造函数
    • (2)构造并使用n个值为value的元素初始化
    • (3)区间构造
    • (4)拷贝构造
  • 三、析构函数
  • 四、[赋值运算符重载](https://so.csdn.net/so/search?q=%E8%B5%8B%E5%80%BC%E8%BF%90%E7%AE%97%E7%AC%A6%E9%87%8D%E8%BD%BD&spm=1001.2101.3001.7020)
  • 五、reserve的实现--改变容器容量大小
  • 五、resize的实现--改变容器有效字符个数
  • 六、几种简单接口(empty、size、capacity、clear)
  • 七、push_back和pop_back的实现
  • 八、insert的实现(重点)--迭代器失效问题一
  • 八、erase的实现(重点)--迭代器失效问题二


前言

类框架与之前string类的模拟实现类似,区别在于vector 是用 _finish 和 _end_of_storage 两个指针来维护空间,但本质上其实是一样的 ,
_size = _finish - _start
_capacity = _end_of_storage - _start

class vector
{
 public:
     //成员函数
 private:
		iterator _start;
		iterator _finish;
		iterator _endofstorage;
}

接下来介绍成员函数的实现。

一、迭代器

iterator 与 const_iterator 的作用:
遍历容器内的元素,并访问这些元素的值。

iterator 与 const_iterator 的区别:
iterator 可以改元素值,但 const_iterator 不可以改元素值。

const_iterator 对象可以用于 const 对象 或非 const 对象,它自身的值可以改(可以指向其他元素),但不能改写其指向的元素值。

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;
		}

二、重载 [ ]

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

三、构造函数相关(重点)

(1)构造函数

直接将所有的成员变量置为空即可

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

(2)构造并使用n个值为value的元素初始化

vector(int n, const T& val = T())
			:_start(nullptr)
			, _finish(nullptr)
			, _endofstorage(nullptr)
		{
			reserve(n);
			for (int i = 0; i < n; ++i)
			{
				push_back(val);
			}
		}

其中,第二个参数val是一个缺省参数。对于没有给定确定值的时候,有两种情况:
(1)如果T是内置类型,T()的值为0;
(2)如果T是自定义类型,T()调用的就是该自定义类型的默认构造函数。
因此,对于自定义类型一定要有默认构造函数,否则会报错!
重点在于第一个参数的类型,只能是int,如果是size_t类型,会报错。原因如下:
接口测试部分
构造函数错误调用问题

(3)区间构造

//迭代器区间构造
		template <class InputIterator>
		vector(InputIterator first, InputIterator last)
			:_start(nullptr)
			, _finish(nullptr)
			, _endofstorage(nullptr)
		{
			while (first != last)
			{
				push_back(*first);
				++first;
			}
		}

为什么单独定义一个这样的函数模板呢?template <class InputIterator>
答:这是因为vector是一个底层连续的容器,它可以用来存储任意类型的数据,
而这里使用区间的方式来初始化vector的容器就需要做到对任意数据都可以进行初始化,因此就需要以模板的形式给出。

(4)拷贝构造

拷贝构造有三种实现方式,最推荐的当然是通过vector内部的swap()函数实现
参数v是const修饰的,它只能调用const类型的成员函数,因此这里必须调用const修饰的迭代器:

//拷贝构造 -- 现代写法
		vector(const vector<T>& v)
			:_start(nullptr)
			, _finish(nullptr)
			, _endofstorage(nullptr)
		{
			vector<T> tmp(v.begin(), v.end()); //复用构造函数和swap函数
			swap(tmp);
		}

//写法2
		//vector(const vector<T>& v)
		//{
		//	T* tmp = new T[v.capacity()];
		//	memcpy(tmp, v._start, sizeof(T) * v.capacity());
		//	_start = tmp;
		//	_finish = _start + v.size();
		//	_end_of_storage = _start + v.capacity();
		//}

//写法3
		//vector(const vector<T>& v)
		//	: _start(nullptr)
		//	, _finish(nullptr)
		//	, _end_of_storage(nullptr)
		//{
		//	reserve(v.capacity());
		//	for (size_t i = 0; i < v.size(); i++)
		//		push_back(v[i]);
		//}


三、析构函数

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

四、赋值运算符重载

vector<T>& operator=(vector<T> v)
		{
			swap(v);
			return *this;
		}

五、reserve的实现–改变容器容量大小

在这里插入图片描述

void reserve(size_t n)
		{
			if (n > capacity())
			{
				size_t oldSize = size();
				T* tmp = new T[n];

				if (_start)
				{
					// 扩容必须深拷贝,否则会导致迭代器失效
					for (size_t i = 0; i < oldSize; ++i)
					{
						tmp[i] = _start[i];
					}
					delete[] _start;
				}

				_start = tmp;
				_finish = tmp + oldSize;
				_endofstorage = _start + n;
			}
		}

五、resize的实现–改变容器有效字符个数

在这里插入图片描述

void resize(size_t n, T val = T())
		{
			if (n > capacity())
			{
				reserve(n);
			}

			if (n > size())
			{
				while (_finish < _start + n)
				{
					*_finish = val;
					++_finish;
				}
			}
			else
			{
				_finish = _start + n;
			}
		}

六、几种简单接口(empty、size、capacity、clear)

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

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

size_t capacity() const
		{
			return _endofstorage - _start;
		}
void clear()
		{
			_finish = _start;
		}

七、push_back和pop_back的实现

void push_back(const T& x)
		{
			if (_finish == _endofstorage)
			{
				size_t newCapacity = capacity() == 0 ? 4 : capacity() * 2;
				reserve(newCapacity);
			}

			*_finish = x;
			++_finish;
		}

void pop_back()
		{
			assert(!empty());

			--_finish;
		}

八、insert的实现(重点)–迭代器失效问题一

//迭代器失效:扩容引起的野指针问题
		iterator insert(iterator pos, const T& val)
		{
			assert(pos >= _start);
			assert(pos < _finish);

			if (_finish == _endofstorage)
			{
				size_t len = pos - _start; //记录pos,避免扩容后pos变为野指针
				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;

			return pos;
		}

迭代器失效问题一:这里是由于insert之后pos 的意义变了–不再指向原来的元素,而是指向新插入的元素。因此,如果不读pos进行更新,pos就会成为野指针。

八、erase的实现(重点)–迭代器失效问题二

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

			iterator begin = pos + 1;
			while (begin < _finish)
			{
				*(begin - 1) = *(begin);
				++begin;
			}
			--_finish;
			return pos;
		}

迭代器失效问题二:这里是由于erase之后pos 的意义变了,分为两种情况:
(1)删除元素后pos 不再指向原来的元素,而是指向该元素的后一个元素,所以 erase 之后会导致一个元素被跳过;
(2)在极端情况下–删除最后一个元素之后,pos 就等于 _finish,会发生越界。

因此insert 和 erase 之后迭代器失效,必须更新后才能再次使用。

具体的更新措施:
在这里插入图片描述

在这里插入图片描述

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

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

相关文章

什么是科学

人人都是价值观-思辨专家_个人渣记录仅为自己搜索用的博客-CSDN博客 相关文章 人人都是中医爱好者 科学定义 关于“科学”这个词的定义&#xff0c;历史上曾出现过多种版本&#xff0c;但是目前为止还没有一个是世人公认的定义。 历史上达尔文(Charles Robert Darwin&#xff…

利用阿里云免费部署openai的Chatgpt国内直接用

背景 国内无法直接访问ChatGPT&#xff0c;一访问就显示 code 1020。而且最近OpenAI查的比较严格&#xff0c;开始大规模对亚洲地区开始封号&#xff0c;对于经常乱跳IP的、同一个ip一堆账号的、之前淘宝机刷账号的&#xff0c;账号被封的可能性极大。 那么有没有符合openai规定…

< element-Ui表格组件:表格多选功能回显勾选时因分页问题,导致无法勾选回显的全部数据 >

文章目录&#x1f449; 前言&#x1f449; 一、解决思路&#x1f449; 二、实现代码&#xff08;仅供参考&#xff0c;具体问题具体分析&#xff09;> HTML模板> Js模板往期内容 &#x1f4a8;&#x1f449; 前言 在 Vue elementUi 开发中&#xff0c;elementUI中表格在…

Linux服务器怎么修改系统时间

Linux服务器怎么修改系统时间 linux服务器的系统时间&#xff0c;有的时候会产生误差&#xff0c;导致我们的程序出现一些延迟&#xff0c;或者其他的一些错误&#xff0c;那么怎么修改linux的系统时间呢&#xff1f; 我是艾西&#xff0c;今天又是跟linux小白分享小知识的时间…

C语言函数大全-- l 开头的函数

C语言函数大全 本篇介绍C语言函数大全-- l 开头的函数 1. labs&#xff0c;llabs 1.1 函数说明 函数声明函数功能long labs(long n);计算长整型的绝对值long long int llabs(long long int n);计算long long int 类型整数的绝对值 1.2 演示示例 #include <stdio.h> …

Python-Python基本用法(全:含基本语法、用户交互、流程控制、数据类型、函数、面向对象、读写文件、异常、断言等)

1 环境准备 编辑器&#xff1a;Welcome to Python.org 解释器&#xff1a;pycharm&#xff1a;Thank you for downloading PyCharm! (jetbrains.com) 2 Quick start 创建项目 new project create demo print(Dad!!)3 基本语法 3.1 print 直接打印 print(Dad!!)拼接打印…

记录-Vue.js模板编译过程揭秘:从模板字符串到渲染函数

这里给大家分享我在网上总结出来的一些知识&#xff0c;希望对大家有所帮助 Vue.js是一个基于组件化和响应式数据流的前端框架。当我们在Vue中编写模板代码时&#xff0c;它会被Vue编译器处理并转换为可被浏览器解析的JavaScript代码。Vue中的模板实际上是HTML标记和Vue指令的组…

STM32HAL库 串口USART的使用

STM32HAL库 串口USART的使用 文章目录STM32HAL库 串口USART的使用前言一、配置USART1串口通信引脚二、使用步骤三、串口中断回调函数1. 配置2. 在icode中增加usart.c和usart.h文件3. 中断处理对比4. 编写串口控制程序总结前言 本文为串口输出打印的hal库&#xff0c;参考洋桃电…

【LeetCode】剑指 Offer 57. 和为 s 的数字 p280 -- Java Version

1. 题目介绍&#xff08;57. 和为 s 的数字&#xff09; 面试题57&#xff1a;和为 s 的数字&#xff0c; 一共分为两小题&#xff1a; 题目一&#xff1a;和为 s 的两个数字题目二&#xff1a;和为 s 的连续正数序列 2. 题目1&#xff1a;和为s的两个数字 题目链接&#xff1…

图结构基本知识

图1. 相关概念2. 图的表示方式3. 图的遍历3.1 深度优先遍历&#xff08;DFS&#xff09;3.2 广度优先遍历&#xff08;BFS&#xff09;1. 相关概念 图G(V,E) &#xff1a;一种数据结构&#xff0c;可表示“多对多”关系&#xff0c;由顶点集V和边集E组成&#xff1b;顶点(vert…

数据库管理-第六十七期 SQL Domain 2(20230414)

数据库管理 2023-04-14第六十七期 SQL Domain 21 Domain函数示例总结第六十七期 SQL Domain 2 昨晚割接&#xff0c;搭了一套19c的ADG&#xff0c;今天睡了个懒觉&#xff0c;早上把笔记本内存扩到了64GB&#xff0c;主要是为了后面做实验。然后下午拼了个乐高&#xff0c;根据…

Excel小技巧:对比两列数据的异同、vlookup使用方法

目录 问题一&#xff1a; 在联盟对接的时候&#xff0c;团购站会推送一个返利值&#xff0c;称为“推送返利”&#xff0c;联盟后台又会计算一个返利值&#xff0c;称为“计算返利”。当团购站的推送返利与计算返利相同的时候&#xff0c;我们才认为这个团购站在返利上对接完…

JMeter全局变量在使用时第一次取到null的问题解决

1. 在执行JMeter测试时&#xff0c;登录操作只需要执行一次。这样就需要用到全局变量&#xff0c;但在前一个提取器执行了请求后&#xff0c;如果返回结果是json格式的数据&#xff0c;会在下面添加一个JSON提取器&#xff0c;并在json提取器下会添加一个后置BeanShell PostPro…

10个镜像网站工具箱供你使用,不注册ChatGPT也能免费使用ChatGPT

ChatGPT已经成为了人工智能技术中备受瞩目的一员&#xff0c;它可以为我们带来更加智能化、个性化的交互体验。对于没有ChatGPT账号或者不想注册账号的人来说&#xff0c;他们可能会错过这种神奇的体验。 而本篇文章就帮大家解决这个问题&#xff0c;不用登录ChatGPT账号&…

Spark 写 MySQL经典50题

目录 建表 & 添加数据 表结构分析图 连接数据库 题目 1、查询"01"课程比"02"课程成绩高的学生的信息及课程分数 2、查询"01"课程比"02"课程成绩低的学生的信息及课程分数 3、查询平均成绩大于等于60分的同学的学生编号和学…

如何将GIS地图和可视化结合使用实现更好的数据呈现

GIS&#xff08;地理信息系统&#xff09;和可视化&#xff08;visualization&#xff09;是两个紧密相关的领域。GIS是一种用于管理、分析和展示地理空间数据的技术&#xff0c;而可视化则是一种用图形、图表、动画等形式展示数据的方式。GIS地图则是指基于地理信息系统技术&a…

【举一反三】只出现一次的数字

本文&#xff0c;讲位运算——异或运算。因为题干中说明要线性时间复杂度&#xff0c;所以采用位运算进行操作&#xff0c;而没有采用哈希表。 目录 1.只出现一次的数字 I 2.只出现一次的数字 II 3.只出现一次的数字 III 1.只出现一次的数字 I 136. 只出现一次的数字 - 力扣&…

浅析Dubbo核心设计

大家好&#xff0c;我是易安&#xff01; 当今互联网时代&#xff0c;随着企业业务的不断扩展和用户量的增加&#xff0c;分布式系统已成为大型企业必不可少的组成部分。而Dubbo框架作为阿里巴巴开源的高性能Java RPC框架&#xff0c;一直以来都备受关注和使用。其核心设计思想…

机器学习正以惊人的速度破解宇宙奥秘

宇航员、科学家和其他以探索并记录终极边界为己任的人们&#xff0c;才积极转向机器学习&#xff08;ML&#xff09;以协助应对自己面临的非凡挑战。从引导火箭穿越太空到研究遥远行星的表面&#xff0c;再到测量宇宙大小和计算天体的运动轨迹&#xff0c;AI在太空中拥有着众多…

300左右蓝牙耳机推荐,口碑好的平价蓝牙耳机选购指南

300元预算&#xff0c;想入手一款最值得蓝牙耳机&#xff0c;咋选&#xff1f;作为一个有4年玩机经验的爱好者&#xff0c;蓝牙耳机推荐性价比高的产品&#xff0c;今天就总结了几款目前很受欢迎&#xff0c;同时性能各方面都不错的机型&#xff0c;选对这几款&#xff0c;不用…