【C++】vector模拟实现

目录

  • 简介:
  • 私有成员:
    • 迭代器:
  • 无参构造函数:
  • push_back:
    • reserve:
    • resize:
    • push_back:
  • operator[]重载:
  • begin && end:
  • size && capacity:
  • insert:
  • erase:
  • 带参构造:
  • 迭代器区间构造:
  • 拷贝构造 && 赋值运算符重载:
  • 析构函数:
  • 深浅拷贝的验证:

简介:

本文将利用C/C++模拟vector常用接口实现,可以更好的帮助理解vector底层。

主体的实现思路是先搭起来一个能跑的架子,再一点点的补充完善。

私有成员:

与我们曾经实现过得顺序表有些不一样,顺序表是一个malloc出来的指针指向数组,另外还有capacity与size

但在这里我们参照vector的原码进行模拟,使用三个迭代器进行控制这个动态数组。

	private:iterator start = nullptr;iterator finish = nullptr;iterator end_of_storage = nullptr;

故因此我们先要了解一下这个迭代器的设计,我们在模拟string时,由于string底层也是数组,由于虚拟地址的存在,我们认为数组在内存是连续的,故可以直接使用原生指针进行模拟迭代器,vector也是数组,我们当然也可以使用原生指针进行模拟迭代器

在这里插入图片描述
关于各个指针的含义

迭代器:

由于我们的vector需要支持不同类型,需要使用模版进行设计,故我们设置迭代器时如下图定义即可。

	template<class T>class vector{public:typedef T* iterator;typedef const T* const_iterator;}

至于为什么设计两个类型迭代器是为了const对象也可以调用,更具体可以看一下string模拟实现中的迭代器。

无参构造函数:

无参的没什么好说的,直接全初始化为nullptr。

		vector():start(nullptr), finish(nullptr), end_of_storage(nullptr){}

push_back:

reserve:

因为push_back等很多操作涉及扩容的问题,故我们可以先实现一个reserve的接口,方便接下来使用:

		void reserve(size_t n){size_t capacity = end_of_storage - start;if (capacity < n){size_t size = finish - start;T* tmp = new T[n];//memcpy(tmp, start, size * sizeof(T));for (int i = 0; i < size; i++){*(tmp + i) = *(start + i);}delete[] start;start = tmp;finish = tmp + size;end_of_storage = tmp + n;}}

关于为什么使用逐个赋值而不是直接memcpy是为了防止浅拷贝,在接下来会有演示

既然都实现了reserve那么resize也就顺便实现一下。

resize:

		void resize(size_t n, const T& val = T()){size_t capacity = end_of_storage - start;if (n > capacity){reserve(n);size_t len = end_of_storage - finish;while (len--){*finish = val;finish++;}}else{finish = start + n;}}

push_back:

		void push_back(const T& val){if (finish == end_of_storage){size_t capacity = end_of_storage - start;reserve(capacity == 0 ? 4 : capacity * 2);}*finish = val;finish++;}

这里针对嵌套类型也不用担心浅拷贝。
如果嵌套string类
*finish是一个string对象,赋值时会调用string内部的深拷贝。

operator[]重载:

		T& operator[](size_t pos){return start[pos];}const T& operator[](size_t pos) const{return start[pos];}

由于const对象也会调用,故我们也要实现const版本的重载。

begin && end:

		iterator begin(){return start;}iterator end(){return finish;}const_iterator end() const{return finish;}const_iterator begin() const{return start;}

size && capacity:

		size_t size(){return finish - start;}size_t capacity(){return end_of_storage - start;}size_t size() const{return finish - start;}size_t capacity() const{return end_of_storage - start;}

insert:

		void insert(iterator pos, const T& val){if (finish == end_of_storage){int distance = pos - start;reserve(capacity() == 0 ? 4 : 2 * capacity());pos = start + distance;}int i = 0;int count = finish - pos;while(count--){*(finish - i) = *(finish - i - 1);i++;}finish++;*pos = val;}

erase:

		void erase(iterator pos){assert(size());int count = finish - pos - 1;int i = 0;while (count--){*(pos + i) = *(pos + i + 1);i++;}finish--;}

带参构造:

		vector(int n, const T& val = T()){reserve(n);while (n--){push_back(val);}}

迭代器区间构造:

		template<class InputIterator>vector(InputIterator first, InputIterator last){while (first != last){push_back(*first);first++;}}

拷贝构造 && 赋值运算符重载:

关于拷贝构造与赋值运算符重载一般有两种实现方案,一般方法与现代方法
下图是现代方法,龙我们已经写好的接口进行拷贝,不用自己开空间,自己赋值等操作。

		vector(const vector<T>& v){for (auto& val : v){push_back(val);}}

而赋值的做法更绝,因为传参不带引用会调用拷贝构造。我们直接利用拷贝构造的成果,进行swap,而等赋值拷贝结束会自动调用析构,帮助我们将旧的vector对象进行解决。

		void swap(vector<T>& v){std::swap(start, v.start);std::swap(finish, v.finish);std::swap(end_of_storage, v.end_of_storage);}vector& operator= (vector<T> val){swap(val);return *this;}

析构函数:

		~vector(){delete[] start;start = finish = end_of_storage = nullptr;}

深浅拷贝的验证:

当我们在vector内放置自定义类型,如果你的程序浅拷贝,那么大概率会报错
在这里插入图片描述
而深浅拷贝都会发生在扩容,我们的扩容依赖的都是reserve这个接口,故这个接口一定不能发生浅拷贝的问题。

就像我们在reserve哪里说的,不能使用memcpy,这样会发生浅拷贝
在这里插入图片描述
解决方法也很简单,利用自定义类型的深拷贝即可

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

matlab使用教程(35)—求解时滞微分方程(3)

1中立型 DDE 以下示例说明如何使用 ddensd 求解中立型 DDE&#xff08;时滞微分方程&#xff09;&#xff0c;其中时滞出现在导数项中。此问题最初由 Paul [1] 提出。方程是&#xff1a; 由于该方程在 y ′ 项中存在时滞&#xff0c;因此该方程称为中立型 DDE。如果时滞仅出现…

Python 与机器学习,在服务器使用过程中,常用的 Linux 命令包括哪些?

&#x1f349; CSDN 叶庭云&#xff1a;https://yetingyun.blog.csdn.net/ 本博客旨在分享在实际开发过程中&#xff0c;开发者需要了解并熟练运用的 Linux 操作系统常用命令。Linux 作为一种操作系统&#xff0c;与 Windows 或 MacOS 并驾齐驱&#xff0c;尤其在服务器和开发环…

使用 RisingWave、NATS JetStream 和 Superset 进行实时物联网监控

在物联网&#xff08;IoT&#xff09;背景下&#xff0c;处理实时数据会遇到一些特定的障碍&#xff0c;如边缘计算资源不足、网络条件限制、扩展性存在问题、设备间有多样性差异。要克服这些挑战&#xff0c;需要高效的边缘计算技术、强大的安全措施、标准化协议、可扩展的管理…

spring中各种bean加载顺序

具体加载顺序按照罗列的顺序 XXXAware ApplicationContextAware、EnvironmentAware、BeanFactoryAware、BeanClassLoaderAware 顾名思义&#xff0c;用于获取对应的对象&#xff0c;需要在实体类中声明对应的对象且当前类为普通类能被注入。 InitializingBean void afterProp…

【软件工程】测试规格

1. 引言 1.1简介 本次的测试用例是基于核心代码基本开发完毕&#xff0c;在第一代系统基本正常运行后编写的&#xff0c;主要目的是为了后续开发与维护的便利性。 该文档主要受众为该系统后续开发人员&#xff0c;并且在阅读此文档前最后先阅读本系统的需求文档、概要设计文…

日志、logback、logback.xml --java学习笔记

什么是日志&#xff1f; 好比生活中的日记&#xff0c;可以记录你生活中的点点滴滴程序中的日志&#xff0c;通常就是一个文件&#xff0c;里面记录的是程序运行过程中的各种信息 之前记录日志的方法都是使用输出语句&#xff1a; 这种方法其实并不适合用来记录日志&#xff…

【c++】初阶模版与STL简单介绍

&#x1f525;个人主页&#xff1a;Quitecoder &#x1f525;专栏&#xff1a;c笔记仓 朋友们大家好&#xff0c;本篇文章介绍一下模版和对STL进行简单的介绍&#xff0c;后续我们进入对STL的学习&#xff01; 目录 模版1.泛型编程2.函数模板2.1函数模板的原理2.2模版的实例化…

实验:基于Red Hat Enterprise Linux系统的创建磁盘和磁盘分区(二、三)

目录 一. 实验目的 二. 实验内容 三. 实验设计描述及实验结果 实验二&#xff1a; 1. 为nvme0n2p1设备建立配额属性和文件(EXT) 2. 要求自己名字的用户只能存储不超过200M的文件&#xff0c;总数量不能大于10个 quotacheck [选项] 文件系统 edquota quotaon [选项] 文件系…

某盾滑块拼图验证码增强版

介绍 提示&#xff1a;文章仅供交流学习&#xff0c;严禁用于非法用途&#xff0c;如有不当可联系本人删除 最近某盾新推出了&#xff0c;滑块拼图验证码&#xff0c;如下图所示&#xff0c;这篇文章介绍怎么识别滑块距离相关。 参数attrs 通过GET请求获取的参数attrs, 决…

背包问题---

一、背包模型 有一个体积为V的背包,商店有n个物品,每个物品有一个价值v和体积w,每个物品只能被拿一次,问能够装下物品的最大价值。 这里每一种物品只有两种状态即"拿"或"不拿". 设状态dp[i][j]表示到第i个物品为止,拿的物品总体积为j的情况下的最大价…

Docker:探索容器化技术,重塑云计算时代应用交付与管理

一&#xff0c;引言 在云计算时代&#xff0c;随着开发者逐步将应用迁移至云端以减轻硬件管理负担&#xff0c;软件配置与环境一致性问题日益凸显。Docker的横空出世&#xff0c;恰好为软件开发者带来了全新的解决方案&#xff0c;它革新了软件的打包、分发和管理方式&#xff…

【智能排班系统】基于SpringSecurity实现登录验证、权限验证

文章目录 SpringSecurity介绍sss-security实现依赖工具类Jwt工具JSON响应工具加密工具类 用户上下文用户信息实体类用户上下文 自定义重写自定义无权限的报错自定义密码加密自定义用户类 过滤器登录过滤器权限过滤器 Service登录Service 配置类说明登录验证权限验证IP流量限制 …

C语言第四十弹---预处理(下)

✨个人主页&#xff1a; 熬夜学编程的小林 &#x1f497;系列专栏&#xff1a; 【C语言详解】 【数据结构详解】 预处理 1、#和## 1.1 #运算符 1.2、##运算符 2、命名约定 3、#undef 4、命令行定义 5、条件编译 6、头文件的包含 6.1、头文件被包含的方式 6.1.1、本地…

Spark 部署与应用程序交互简单使用说明

文章目录 前言步骤一&#xff1a;下载安装包Spark的目录和文件 步骤二&#xff1a;使用Scala或PySpark Shell本地 shell 运行 步骤3:理解Spark应用中的概念Spark Application and SparkSessionSpark JobsSpark StagesSpark Tasks 转换、立即执行操作和延迟求值窄变换和宽变换 S…

StreamingT2V文本生成视频多模态大模型,即将开源!

1、前言 Picsart人工智能研究所、德克萨斯大学和SHI实验室的研究人员联合推出了StreamingT2V视频模型。通过文本就能直接生成2分钟、1分钟等不同时间&#xff0c;动作一致、连贯、没有卡顿的高质量视频。 虽然StreamingT2V在视频质量、多元化等还无法与Sora媲美&#xff0c;但…

【鹅厂摸鱼日记(一)】(工作篇)认识八大技术架构

&#x1f493;博主CSDN主页:杭电码农-NEO&#x1f493;   ⏩专栏分类:重生之我在鹅厂摸鱼⏪   &#x1f69a;代码仓库:NEO的学习日记&#x1f69a;   &#x1f339;关注我&#x1faf5;带你学习更多知识   &#x1f51d;&#x1f51d; 认识八大架构 1. 前言2. 架构简介&…

uniapp:小程序腾讯地图程序文件qqmap-wx-jssdk.js 文件一直找不到无法导入

先看问题&#xff1a; 在使用腾讯地图api时无法导入到qqmap-wx-jssdk.js文件 解决方法&#xff1a;1、打开qqmap-wx-jssdk.js最后一行 然后导入&#xff1a;这里是我的路径位置&#xff0c;可以根据自己的路径位置进行更改导入 最后在生命周期函数中输出&#xff1a; 运行效果…

159 Linux C++ 通讯架构实战14,epoll 函数代码实战

ngx_epoll_init函数的调用 //&#xff08;3.2&#xff09;ngx_epoll_init函数的调用&#xff08;要在子进程中执行&#xff09; //四章&#xff0c;四节 project1.cpp&#xff1a;nginx中创建worker子进程&#xff1b; //nginx中创建worker子进程 //官方nginx ,一个…

为“自研”的KV数据库编写JDBC驱动

一觉醒来&#xff0c;受到梦的启发&#xff0c;自研了一套K/V数据库系统&#xff0c;因为"客户"一直催促我提供数据库的JDBC驱动&#xff0c;无奈之下&#xff0c;只好花费一个上午的时间为用户编写一个。 我们知道&#xff0c;JDBC只定义一系列的接口, 具体的实现需…

python 利用xpath 爬取一周天气

需求&#xff1a; 爬取 中国天气网指定城市一周的天气&#xff0c;以天津为例 实现&#xff1a; 1&#xff0c;先找到一周的数据位置。 divs html.xpath("//div[classhanml]") 2&#xff0c;再遍历每天。 trs div.xpath("./div/div[2]/table//tr[position…