【C++】vector 模拟笔记

news2024/11/29 19:49:02

文章目录

  • 成员变量和迭代器
  • reserve()函数易错点
  • 迭代器区间初始化易错点
  • 迭代器失效
  • 整体代码

成员变量和迭代器

  下面有vector 存储示意图:vector 是一个左闭又开的空间,_finish 不能存储有效数据。vector 的 iteratorT 类型的指针,不要认为 iterator 就是指针,而且后面还有 list,map,set 等各种容器的迭代器,它们都不是原生指针。在这里我们 vector 写成模板,同时不能与库里面的空间产生冲突,我们需要自己定一个命名空间,具体代码看最下面的整体带代码。

reserve()函数易错点

  扩容是异地扩容,需要开一块新的空间,然后把数据拷贝过去,再将原来的空间释放掉。这里注意必须开始首位指针的相对位置,异地扩容tmp赋值给_start,此时 _start和_finish 并不指向同一块空间,而 size() 却是由 _start和_finish 计算而来,由此出错。

错误代码: _finish=_start+size();

void reserve(size_t n)
{
	if (n > capacity())
	{
		size_t len = size();
		T* tmp = new T[n];
		if (_start)
		{
			for (size_t i = 0; i < size(); i++)
			{
				tmp[i] = _start[i];
			}
			delete[] _start;
		}
		_start = tmp;
		_finish = _start + len;
		_end_of_storage = _start + n;
	}
}

  假若对象是自定义类型那么还要完成深拷贝,深拷贝不能用 memcpy 拷贝,比如对象是 stringmemcpy 是值拷贝只会把*string对象的指针地址拷贝给另外一个对象,导致两个string对象的_str指针指向同一块数组空间,而析构两次。因此我们需要调用string类自己的赋值运算符重载函数进行赋值

错误代码: memcpy(_start, v._start, sizeof(T)*v.size() );

迭代器区间初始化易错点

  当 T 类型为 int,运行这句代码 vector v1(10, 1); 就会报下面这个错误,说明它并没有调用上面这个函数。因为第一个形参为 size_t ,第二个实例化为 int,而实参两个都为int,且第二个用迭代器区间初始化函数,两个形参相同,此时更匹配第二个函数。调用第二个函数后对 int 解引用而报错

错误代码: vector(size_t n, const T& val = T() );

vector(int n, const T& val = T())
{
	//参数用int而不用size_t避免当两个参数都为int时,与下面的用迭代器初始化函数冲突
	resize(n, val);
}
//用迭代器区间初始化
template<class in_T>
vector(in_T first, in_T last)
{
	while (first != last)
	{
		push_back(*first);
		++first;
	}
}

迭代器失效

  由上面的扩容逻辑可以知道,插入数据有时候需要扩容,而扩容是异地扩容,原来空间已经被释放了,那么指向原来空间的迭代器自然失效了。因此我们要更新迭代器,让 insert()和erase() 函数返回新的迭代器,新的迭代器和原来迭代器到 _start 的相对距离相等。虽然有时候插入迭代器并没有失效,但是考虑不同平台实现的封装和代码的可移植性,默认它就是会失效,vs库里面实现的也是默认失效,继续用直接报错

整体代码

#pragma once
#include <assert.h>
namespace Me
{
	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
		size_t capacity() const
		{
			return _end_of_storage - _start;
		}
		size_t size() const
		{
			return _finish - _start;
		}

		//重载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];
		}

		//无参构造
		vector()
		{}
		//深拷贝
		//不用memcpy拷贝,因为对象可能是自定义类型,比如<string>,memcpy是值拷贝,
		//会导致两个string对象的_str指针指向同一块数组空间
		//而析构两次,因此我们需要调用赋值运算符重载函数进行拷贝
		
		//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 + v.size();
		//	_end_of_storage = _start + v.capacity();
		//}

		//现代写法
		vector(const vector<T>& v)
		{
			reserve(v.capacity());
			for (auto& e : v)
			{
				//会自动更新_finish指针
				push_back(e);
			}
		}
		//赋值运算符重载
		vector<T>& operator=(vector<T> v)
		{
			std::swap(_start, v._start);
			std::swap(_finish, v._finish);
			std::swap(_end_of_storage, v._end_of_storage);
			return *this;
		}
		
		vector(int n, const T& val = T())
		{
			//参数用int而不用size_t避免当两个参数都为int时,与下面的用迭代器初始化函数冲突
			resize(n, val);
		}
		//用迭代器区间初始化
		template<class in_T>
		vector(in_T first, in_T last)
		{
			while (first != last)
			{
				push_back(*first);
				++first;
			}
		}

		//析构
		~vector()
		{
			if (_start)
			{
				delete[] _start;
				_start = _finish = _end_of_storage = nullptr;
			}
		}

		//扩容和调整
		void reserve(size_t n)
		{
			if (n > capacity())
			{
				//记录原来相对位置,后面_start和_finish并不指向同一块空间
				size_t len = size();
				T* tmp = new T[n];
				if (_start)
				{
					for (size_t i = 0; i < size(); i++)
					{
						//这里和上面一样,必须深拷贝
						tmp[i] = _start[i];
					}
					delete[] _start;
				}
				_start = tmp;
				_finish = _start + len;
				_end_of_storage = _start + n;
			}
		}
		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++;
				}
			}
		}

		//打印
		void print()
		{
			for (auto e : *this)
			{
				cout << e << " ";
			}
			cout << endl;
		}

		//插入 迭代器使用一次后可能会失效,
		//因此要返回新的迭代器,旧的默认失效处理
		iterator insert(iterator pos, const T& x)
		{
			assert(pos >= _start && pos <= _finish);
			if (_finish == _end_of_storage)
			{
				//扩容需要记录相对位置,以免迭代器失效
				size_t len = pos - _start;
				reserve(capacity() == 0 ? 4 : capacity() * 2);
				pos = _start + len;//更新迭代器
			}
			T* end = _finish;
			_finish++;
			while (end >= pos)
			{
				*end = *(end - 1);
				--end;
			}
			*pos = x;
			return pos;
		}
		void push_front(const T& x)
		{
			insert(begin(), x);
		}
		void push_back(const T& x)
		{
			if (_finish == _end_of_storage)
			{
				reserve(capacity() == 0 ? 4 : capacity() * 2);
			}
			*_finish = x;
			_finish++;
		}

		//删除 
		iterator erase(iterator pos)
		{
			assert(pos >= _start && pos < _finish);
			iterator end = pos;
			_finish--;//先-- 因为_finish不存储有效数据
			while (end < _finish)
			{
				*end = *(end + 1);
				end++;
			}
			return pos;
		}
		void pop_back()
		{
			--_finish;
		}
		void pop_front()
		{
			erase(begin());
		}

	private:
		T* _start = nullptr;
		T* _finish = nullptr;
		T* _end_of_storage = nullptr;
	};

}

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

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

相关文章

Python自动获取字母站视频

如果有疑问的话可以在我的谈论群&#xff1a;706128290 来找我 目录 前言 二、编写代码 1.引入库 2.编写主类 3. 自动获取cookies值和生成headers 4.获取命令行参数 运行效果 前言 browser_cookie3 第三方模块 browser_cookie3是browser_cookie模块的分支&#xff0c;…

Asymmetric Gained Deep Image Compression With Continuous Rate Adaptation文献复现

前言 相关论文阅读自行解决,这里主要是记录代码的学习与实验的复现 github地址 此代码非官方部署代码,而是私人实现的。 本博客仅做学习记录。 1 代码学习 1.1 主要框架部分 这里的主编解码器与高斯建模的方式,采用的是同joint上下联合自回归一样的方式,主要的改动在增益…

【Linux】使用云服务器搭建Linux环境

目录 1.Linux是什么 2.Linux的环境搭建 3.使用Xshell登录主机 1.Linux是什么 Linux&#xff0c;全称GNU/Linux&#xff0c;是一套免费使用和自由传播的类Unix操作系统&#xff0c;是一个基于POSIX的多用户、多任务、支持多线程和多CPU的操作系统。 简单来说&#xff0c;Lin…

图文详解Java参数传递类型

大家好,我是爱编程的喵喵。双985硕士毕业,现担任全栈工程师一职,热衷于将数据思维应用到工作与生活中。从事机器学习以及相关的前后端开发工作。曾在阿里云、科大讯飞、CCF等比赛获得多次Top名次。现为CSDN博客专家、人工智能领域优质创作者。喜欢通过博客创作的方式对所学的…

Linux用户和用户组

Linux是多用户、多任务操作系统 一般来说&#xff0c;一个服务分配一个用户&#xff0c;这个用户只拥有这个服务相关文件的相关权限&#xff0c;如果用户不需要登陆&#xff0c;连密码都可以不分配&#xff0c;一般来说&#xff0c;非法分子都是获取当前进程的的权限来攻击服务…

自然语言处理: 第五章Attention注意力机制

自然语言处理: 第五章Attention注意力机制 理论基础 Attention&#xff08;来自2017年google发表的[1706.03762] Attention Is All You Need (arxiv.org) &#xff09;&#xff0c;顾名思义是注意力机制&#xff0c;字面意思就是你所关注的东西&#xff0c;比如我们看到一个非…

vue3中echarts的使用

效果&#xff1a; 代码&#xff1a; <div class"outcharbox"><a-row :gutter"10"><a-col :span"8" v-for" (item, index) in linesobjdata" :key"item.MonitorItemId"><monitoringItemsChart :colorI…

49天精通Java,第40天,jd-gui反编译class文件,解决jd-gui中文乱码问题

目录 专栏导读一、添加局部变量二、反编译class文件三、解决乱码问题四、产品经理就业实战1、内容简介2、作者简介 专栏导读 本专栏收录于《49天精通Java从入门到就业》&#xff0c;本专栏专门针对零基础和需要进阶提升的同学所准备的一套完整教学&#xff0c;从0开始&#xf…

【SQL应知应会】表分区(四)• MySQL版

欢迎来到爱书不爱输的程序猿的博客, 本博客致力于知识分享&#xff0c;与更多的人进行学习交流 本文收录于SQL应知应会专栏,本专栏主要用于记录对于数据库的一些学习&#xff0c;有基础也有进阶&#xff0c;有MySQL也有Oracle 分区表 • MySQL版 前言一、分区表1.非分区表2.分区…

系统学习Linux-SSH远程服务(二)

概念 安全外壳协议&#xff0c;提供安全可靠的远程连接 特点 ssh是工作在传输层和应用层的协议 ssh提供了一组管理命令 ssh 远程登陆 scp 远程拷贝 sftp 远程上传下载 ssh-copy-id ssh keygen 生成 提供了多种身份验证机制 身份验证机制 密码验证 需要提供密码 密…

vue element select下拉框回显展示数字

vue element select下拉框回显展示数字 问题截图&#xff1a; 下拉框显示数字可以从数据类型来分析错误&#xff0c;接收的数据类型是字符串&#xff0c;但是value是数字类型 <el-form-item prop"classifyLabelId" :label"$t(item.classifyLabelId)"…

051、事务设计之TiDB事务实现方式

事务在TiDB中的存储 分布式事务 提交的第一阶段&#xff0c;会用三个CF 来存放这些数据信息&#xff0c; 一类列簇对应一类键值对&#xff0c; 第一个CF(default)存放的是数据 的键值对。第二个存放的是锁信息。 第三个对应的是提交信息。 put<3_100,Frank> 3_100: prim…

LeetCode·每日一题·1851. 包含每个查询的最小区间·优先队列(小顶堆)

题目 示例 思路 离线查询&#xff1a; 输入的结果数组queries[]是无序的。如果我们按照输入的queries[]本身的顺序逐个查看&#xff0c;时间复杂度会比较高。 于是&#xff0c;我们将queries[]数组按照数值大小&#xff0c;由小到大逐个查询&#xff0c;这种方法称之为离线查询…

Go语言之接口(interface)

1.1 、多态的含义 在java里&#xff0c;多态是同一个行为具有不同表现形式或形态的能力&#xff0c;即对象多种表现形式的体现&#xff0c;就是指程序中定义的引用变量所指向的具体类型和通过该引用变量发出的方法调用在编程时并不确定&#xff0c;而是在程序运行期间才确定&am…

T5模型: Transfer Text-to-Text Transformer(谷歌)

&#x1f525; T5由谷歌发表于2019&#xff0c;《Exploring the Limits of Transfer Learning with a Unified Text-to-Text Transformer》&#xff0c;最终版本发布在&#xff1a;JMLR。 一句话总结T5: 大一统模型&#xff0c;seq2seq形式完成各类nlp任务&#xff0c;大数据集…

Docker 的前世今生:从社区到市场,从领域到技术应用的全方位分析

博主 默语带您 Go to New World. ✍ 个人主页—— 默语 的博客&#x1f466;&#x1f3fb; 《java 面试题大全》 &#x1f369;惟余辈才疏学浅&#xff0c;临摹之作或有不妥之处&#xff0c;还请读者海涵指正。☕&#x1f36d; 《MYSQL从入门到精通》数据库是开发者必会基础之…

下载编译Chromium

参考&#xff1a;Mac上本地编译Chrome浏览器踩坑笔记&#xff08;2021.02最新&#xff09; - 掘金 For Mac: 一、下载编译工具链&#xff1a;deptool git clone https://chromium.googlesource.com/chromium/tools/depot_tools.git export PATH"$PATH:/Users/yumlu/co…

jib进行本地打包,并上传本地镜像仓库

使用 Jib 进行本地打包和上传到本地镜像仓库是一种方便的方式&#xff0c;而无需编写 Dockerfile。Jib 是一个开源的 Java 容器镜像构建工具&#xff0c;它可以直接将 Java 项目打包为镜像&#xff0c;并将其推送到容器镜像仓库。 gradle 进行jib的配置 import java.time.Zon…

第53步 深度学习图像识别:Bottleneck Transformer建模(Pytorch)

基于WIN10的64位系统演示 一、写在前面 &#xff08;1&#xff09;Bottleneck Transformer "Bottleneck Transformer"&#xff08;简称 "BotNet"&#xff09;是一种深度学习模型&#xff0c;在2021年由Google的研究人员在论文"Bottleneck Transfor…

MaxCompute与 Mysql 之单字段转多行

在实际数据处理中&#xff0c;可能会遇到行列转换的数据处理&#xff0c;在 MaxCompute 与 AnalyticDB MySQL 数据处理与转换 介绍过如多行转一行&#xff0c;本篇主要介绍将逗号分割的字段转成多行。 一、MaxCompute 实现方式 在MaxCompute中有TRANS_ARRAY函数&#xff0c;可…