【C++】---STL之vector的模拟实现

【C++】---STL之vector的模拟实现

  • 一、vector在源码中的结构:
  • 二、vector类的实现:
    • 1、vector的构造
    • 2、析构
    • 3、拷贝构造
    • 4、赋值运算符重载
    • 5、迭代器
    • 6、operator[ ]
    • 7、size()
    • 8、capacity()
    • 9、reserve()
    • 10、resize()
    • 11、empty()
    • 12、push_back()
    • 13、pop_back()
    • 14、insert()
    • 15、erase()

一、vector在源码中的结构:

在这里插入图片描述
vector的成员变量:
start,finish,end_of_storage的返回类型都是迭代器!
在这里插入图片描述
他的成员变量是三个迭代器:
(1)start:指向第1个元素(开始)
(2)finish:指向数据的结束
(3)end_of_storage:指向空间的结束

二、vector类的实现:

实现以下vector相关内容:
在这里插入图片描述

1、vector的构造

1、无参数构造,构造出一个空容器:

//1、构造函数:vector()// 空的构造函数,所有变量都初始化为空!:_start(nullptr),_finish(nullptr), _end_of_storage(nullptr){}

2、填充构造函数 向容器中插入n个值为val的元素

// 1.1 填充构造函数 向容器中插入n个值为val的元素vector(size_t n, const T& val):_start(nullptr), _finish(nullptr), _end_of_storage(nullptr){reserve(n);while (n){push_back(val);n--;}}

3、完整代码:

#include<iostream>
#include<string>using namespace std;namespace yjl
{template<class T>class vector{private:iterator _start;iterator _finish;iterator _end_of_storage;public://1、构造函数:vector()// 空的构造函数,所有变量都初始化为空!:_start(nullptr),_finish(nullptr), _end_of_storage(nullptr){}// 1.1 填充构造函数 向容器中插入n个值为val的元素vector(size_t n, const T& val):_start(nullptr), _finish(nullptr), _end_of_storage(nullptr){reserve(n);while (n){push_back(val);n--;}}};
}

2、析构

// 2、析构~vector(){if (_start){delete[] _start;// 释放空间!}_start = _finish = _end_of_storage = nullptr;// 对vector的三个迭代器置空!}

3、拷贝构造

1、传统的写法:

注意: 当我们不写vector的拷贝构造函数,编译器会自动生成一份,但编译器生成的只能完成数据的浅拷贝,然而vector的三个成员变量都是迭代器,也就是指针T*,如果T是内置类型,那么就不会出现问题,但是如果T是自定类型,会出现拷贝对象和被拷贝对象指向同一块空间,根据后定义的先析构的原则,此程序会析构两次,会导致程序崩溃

	// 3、拷贝构造:// (1)传统写法:v1(v)vector(const vector<T>& v){// 1.申请空间_start = new T[v.capacity()];// 2.拷贝数据for (int i=0;i<v.size();i++){_start[i] = v._start[i];}// 3.更新数据_finish = _start + v.size();_end_of_storage = _start + v.capacity();}

2、现代的拷贝构造:开空间+逐个尾插

使用现代的拷贝构造时必须初始化,否则_start、_finish、_end_of_storage都是随机值,拷贝数据时可能会导致越界。如果T是自定义类型,那么会调用T的拷贝构造函数进行深拷贝

//(2)现代写法:复用vector(const vector<T>& v):_start(nullptr), _finish(nullptr), _end_of_storage(nullptr){// 1.先开空间:reservereserve(v.capacity());// 2.逐个尾插for (auto& e : v){push_back(e);}}

4、赋值运算符重载

1、传统的赋值运算符重载:

	// 4、赋值运算符重载// (1)传统写法:vector<T> operator=(vector<T> v){if (this != v){delete[] _start;// 如果赋值运算符(=),左右两边不相等,我直接把调用该函数的this清空,//然后把右边的值赋给左边,后面的代码相当于前面的拷贝构造// 1.申请空间_start = new T[v.capacityu()];// 2.拷贝数据for (size_t i = 0; i < v.size(); i++){_start = v._start[i];}// 3.更新数据_finish = _start + v.size();_end_of_storage = _start + v.capacity();}}

2、现代写法:

		// (2)现代写法:// v1 = v2  (在成员函数中,v1调用operator=,v1就是this指针,v2相当于 v )vector<T>& operator=(vector<T> v)//这里面的参数故意没有传引用,目的就是不改变原来v2的值!!!{swap(v);//swap是库里的的成员函数,有隐含的this指针!(即:v1 )return *this;}viod swap(vector<T> v){std::swap(_start, v._start);std::swap(_finish, v._finish);std::swap(_end_of_storage, v._end_of_storage);}

5、迭代器

(1)普通迭代器

iterator begin(){return _start;}iterator end(){return _finish;}

(2)const迭代器

const iterator begin()const{return _start;}const iterator end()const{return _finish;}

6、operator[ ]

//6、 operator[ ]T& operator[](size_t i){assert(i < size());return _start[i];}const T& operator[](size_t i)const{assert(i < size());return _start[i];}

7、size()

// 7、size()size_t size(){return _finish - _start;}

8、capacity()

// 8、capacity()size_t capacity(){return _end_of_storage - _start;}

9、reserve()

1.开辟新空间

2.拷贝数据

3.释放旧空间

4.更新数据

	// 9.reservevoid reserve(size_t n){size_t old_size = size();// 1.开辟新空间T* tmp = new T[n];// 2.拷贝数据for (size_t i = 0; i < old_size; i++){tmp[i] = _start[i];}// 3.释放旧空间delete[] _start;// 4.更新数据_start = tmp;_finish = _start + old_size;_end_of_storage = _start + n;}

为啥要事先保存一个old_size???

注意:错误案例:
在这里插入图片描述
在这里插入图片描述

10、resize()

(1)当resize的大小比原来小,说明空间够,只需要修改大小即可

(2)当resize的大小比原来大,说明空间不够,同时也说明容量可能不够,要判断是否需要申请容量

// 10、resize()void resize(size_t n, const T&  val= T())// 匿名函数的使用:{if (n > size()){// 扩容:reserve(n);while (_finish < _start + n){*_finish = val;_finish++;}}else{_finish = _start + n;}}

11、empty()

// 11、emptybool empty(){return _start == _finish;//起始空间是否为结束空间}

12、push_back()

尾插时,需要:

(1)判断增容

(2)赋值

(3)更新大小

// 12、push_back()void push_back(const T& val){// 1、判断是否需要扩容if (_finish == _end_of_storage){int newcapacity = capacity()==0 ? 4 : capacity() * 2;reserve(newcapacity);}// 2.赋值:*_finish = val;// 3.更新数据:_finish++;}

13、pop_back()

尾删:

(1)判空

(2)直接更新大小

// 13、pop_backvoid pop_back(){assert(!empty());_finish--;}

14、insert()

1、先断言要插入的pos的位置要在start和finish范围之内。

2、然后再判断是否需要扩容,如果进行了扩容,那么就需要先保存要插入的pos位置到start之间的相对位置。(因为如果进行了扩容操作,那么原来的start指针就会被销毁,所以说要先保存相对位置。)

3、定义一个指针,从尾部往前遍历直到post位置的下一个元素,依次往后挪动。

// 14、insertvoid insert(iterator pos, const T& val){// 1、先断言assert(pos >= _start);assert(pos <= _finish);// 2、判断是否需要扩容if (_finish == _end_of_storage){size_t len = pos - _start;reserve(capacity() == 0 ? 4 : capacity() * 2);pos = _start + len;}// 3、将要插入pos的位置,后面的数据依次往后挪。iterator it = _finish - 1;while (it >= pos){*(it + 1) = *it;it--;}_start[pos] = val;_finish++;}

15、erase()

1、先断言检查要删除位置pos是否在start和finish之内。

2、然后再要删除位置的下一个位置定义一个指针,然后从这个指针到结尾的位置的数据依次往前挪动覆盖数据。

3、_finish- -

// 15、erasevoid erase(iterator pos){//1、先断言assert(pos >= _start);assert(pos <= _finish);//2、定义挪动数据的指针iterator it = pos + 1;while (it < _finish){*(it-1) = *it ;it++;}_finish--;}

好了,今天的分享就到这里了
如果对你有帮助,记得点赞👍+关注哦!
我的主页还有其他文章,欢迎学习指点。关注我,让我们一起学习,一起成长吧!
在这里插入图片描述

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

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

相关文章

基于JAVA的机场航班起降与协调管理系统

毕业设计&#xff08;论文&#xff09;任务书 第1页 毕业设计&#xff08;论文&#xff09;题目&#xff1a; 基于JAVA的机场航班起降与协调管理系统 毕业设计&#xff08;论文&#xff09;要求及原始数据&#xff08;资料&#xff09;&#xff1a; 1&#xff0e;综述机场航班调…

JRebel热部署SpringBoot+MyBatis-Plus实现不重启更新修改后MyBatis的XML文件

安装JRebel热部署插件 《JRebel插件安装教程》 《JRebel mybatisPlus extension下载Zip离线安装》 在线安装JRebel mybatisPlus extension 插件商店直接搜JRebel mybatisPlus extension pom.xml 引入依赖 <dependency><groupId>com.baomidou</groupId>…

裤裤带你一起学C语言内存函数啦!

目录 1.memcpy的使用和模拟实现 2.memmove使用和模拟实现 3.memset函数的使用 4.memcmp函数的使用 内存函数在<string.h>库中&#xff0c;我们使用内存函数必须先引入<string.h>头文件 1.memcpy的使用和模拟实现 memcpy的函数原型如下&#xff1a; void * m…

大模型热潮下,AI数据集的重要性及发展现状

2023年3月14日&#xff0c;OpenAI发布了多模态大模型的最新版本GPT-4&#xff1a;文生视频大模型Sora&#xff0c;Sora通过采用更大规模的数据集进行训练,其推理能力超过了ChatGPT&#xff0c;已成为目前最成功、最受欢迎的大模型。 Sora的出现&#xff0c;打破了人们对AI技术…

sketchup{su}安装错误1402

错误如图 解决方法如下 打开autoremove&#xff0c;点击扩展&#xff0c;输入1402&#xff0c;点击搜索 等待修复成功既可尝试重新安装su 软件每周六选择其他方式登录免费使用

SCP收容物091~100

注 &#xff1a;此文接SCP收容物081~090,本文只供开玩笑 ,与steve_gqq_MC合作。 --------------------------------------------------------------------------------------------------------------------------------- 目录 scp-091 scp-092 scp-093 scp-094 scp-095…

RF高频腔设计(7)

3.11 高阶模 之前我们说过&#xff0c;由于束流负载或者其它的原因会在RF高频腔中激发出很多模式&#xff0c;这些模式可能会对束流的稳定性造成不利的影响&#xff0c;尤其是腔的 R / Q ∗ Q L R/Q*Q_L R/Q∗QL​很高时。 为了解决这个问题&#xff0c;需要使用HOM阻尼器来抑…

缓解工作压力的小窍门:保持健康与创新

目录 1 前言2 工作与休息的平衡3 保持心理健康4 社交与网络建设5 结语 1 前言 作为程序员&#xff0c;我们常常承受着高度的工作压力和持续的创新挑战。为了保持高效和健康&#xff0c;我们需要采取一些方法来缓解工作压力&#xff0c;同时促进个人的心理和身体健康。 2 工作…

Android—— log的记忆

一、关键log 1.Java的 backtrace(堆栈log) 上述是一个空指针异常&#xff0c;问题出现在sgtc.settings&#xff0c;所以属于客户UI问题。 2.WindowManager(管理屏幕上的窗口和视图层次结构) 3.ActivityManager(管理应用程序生命周期和任务栈) 4.wifi操作 (1) 连接wifi&#…

安卓原生项目工程结构说明

.gradle 和 .idea (自动生成) .gradle 是gradle下载好的缓存&#xff0c;如果有配置好的 下载好的缓存 直接会拿来用 没有会下载 生成 .idea 是编辑器的配置 app 代码主逻辑 目录 项目中的代码 资源都会在里面 工作的时候的核心目录 gradle 下载安卓的构建器gradle相关的配置信…

Linux c++ onvif客户端开发(8):GetServices

本文是Linux c onvif客户端开发系列文章之一&#xff1a; Linux c onvif客户端开发(1): 根据wsdl生成cpp源文件Linux c onvif客户端开发(2): 获取摄像头H264/H265 RTSP地址Linux c onvif客户端开发(3): 扫描设备Linux c onvif客户端开发(4): 扫描某个设备是否支持onvifLinux c…

目标检测——野火数据集

引言 亲爱的读者们&#xff0c;您是否在寻找某个特定的数据集&#xff0c;用于研究或项目实践&#xff1f;欢迎您在评论区留言&#xff0c;或者通过公众号私信告诉我&#xff0c;您想要的数据集的类型主题。小编会竭尽全力为您寻找&#xff0c;并在找到后第一时间与您分享。 …

八股中的记录

1. protected修饰符&#xff1a;同包或子类&#xff08;不同包&#xff09; 区分普通人和专业人调用的一些方法 2. 抽象&#xff1a;abstract修饰类和方法 抽象类不可实例化&#xff0c;避免错误的new对象 抽象方法是用abstract修饰的方法声明&#xff0c;没有方法体&#xff…

【k8s】Kubernetes 1.29.4离线安装部署(总)

&#xff08;一&#xff09;kubernetes1.29.4离线部署之-安装文件准备 &#xff08;二&#xff09;kubernetes1.29.4离线部署之-镜像文件准备 &#xff08;三&#xff09;kubernetes1.29.4离线部署之-环境初始化 &#xff08;四&#xff09;kubernetes1.29.4离线部署之-组件安装…

好看的混淆矩阵

网上绘制的混淆矩阵都不太满意。。。经过本人多次实验&#xff0c;如下混淆矩阵最为美观&#xff0c;特此记录 import matplotlib matplotlib.use(Agg) from matplotlib import rcParamsimport matplotlib.pyplot as plt import numpy as np# 给定混淆矩阵 cm np.array([[683…

使用可穿戴传感器和设备进行压力检测——综述摘录

用于压力检测的设备&#xff1a; Empatica E4腕带&#xff1a;该设备是腕带&#xff0c;是实时生理数据流和可视化传感器。作为医疗级可穿戴设备&#xff0c;它使研究人员能够收集多种生理数据&#xff0c;例如用于HRV分析的BVP&#xff0c;以及反映皮肤特定区域不断波动的电特…

2024用云渲染好还是自己渲染好!

​cg动画的渲染上经常需要使用到“云渲染”&#xff0c;当然也有小型的动画渲染使用的自己本地电脑渲染。云渲染可节省时间&#xff0c;减少硬件投资&#xff1b;本地电脑渲染就比较灵活的&#xff0c;但是需要高配置的硬件支持&#xff0c;那么两者那个好呢&#xff0c;一起来…

第一个Spring Boot程序

目录 一、Spring Boot介绍 二、创建Spring Boot项目 1、插件安装&#xff08;专业版不需要&#xff09; 2、创建SpringBoot项目 &#xff08;1&#xff09;这里如果插件下载失败&#xff0c;解决方案&#xff1a; &#xff08;2&#xff09;项目启动失败&#xff0c;解决…

skywalking-agent 代码调试

之前已经将 oap 和 ui 调试成功了&#xff0c;今天排查一个 agent 的采集问题&#xff0c;又研究了下 agent 代码如何调试&#xff0c;这下终于齐活了&#xff0c;以后 skywalking 再有问题&#xff0c;直接使用调试大法。 废话不多说&#xff0c;我们看下如何搞&#xff0c;步…

SpringBoot中异步执行事务REQUIRED和REQUIRED_NEW的区别

springboot中事务的使用方式 在spring的bean中&#xff0c;要开启数据库事务只需要在方法中标注注解 Transactional 注解常用的参数有 Class[] rollbackFor() default {} 指定回滚异常Propagation propagation() default Propagation.REQUIRED 事务的传播机制Isolation iso…