当前位置: 首页 > news >正文

C++ vector模拟实现

        

目录

        一.默认成员函数

        二.扩容相关函数

        三.[]重载

        四.修改函数

        五.迭代器


        继上次写完string之后,可以写一个vector练练手以及熟悉其底层。vector是一个顺序表,相比普通数组不同点在于顺序表的数据必须是连续存放的。

        一.默认成员函数

        string是只存放字符,而vector需要存放内置类型和自定义类型,所以需要引入类模板。以下是vector的最外层框架。

    template<class T>//类模板参数,T是接收外面转进来的类型,可以使内置也可以是自定义类型class vector{public://成员函数的定义private://内置成员变量是三个迭代器,vector的迭代器实际上还是指针iterator _start = nullptr; // 指向数据块的开始iterator _finish = nullptr; // 指向有效数据的尾iterator _endOfStorage = nullptr; // 指向存储容量的尾};

        以下是vector的默认成员函数,重载&和const &重载一般不需要手动写。

        vector()//默认构造函数{}vector(int n, const T& value = T())//构造一个包含n个元素的vector,每个元素都是value{resize(n, value);//直接用resize即可}template<class InputIterator>vector(InputIterator first, InputIterator last)//通过一个迭代器区间构造vector{while (first != last){push_back(*first);first++;}}vector(const vector<T>& v)//拷贝构造函数{vector tmp(v.begin(), v.end());//复用迭代器区间构造函数完成拷贝,然后再交换swap(tmp);}vector<T>& operator= (vector<T> v)//通过传值来完成拷贝,然后再交换{swap(v);return *this;}~vector()//析构函数{delete[] _start;//直接delete,由于是delete[]所以即使是多层嵌套(自定义类型或者vector或其他类型)也会调用他们的析构函数(这里需了解delete[]原理)_start = nullptr;_finish = nullptr;_endOfStorage = nullptr;}

         二.扩容相关函数

        扩容函数需要注意异地扩容时把原来空间的元素拷贝到新空间时需要使用赋值而不是memcpy或memmove。对于自定义类型并且实现了深拷贝的赋值就不会出现因为浅拷贝导致多次析构的问题。

        size_t size() const{return _finish - _start;//返回当前元素个数}size_t capacity() const{return _endOfStorage - _start;//返回总容量大小}void reserve(size_t n)//扩容{if (n > capacity())//判断目标扩容大小有没有大于总容量{T* tmp = new T[n];//直接创建新空间,大小为nsize_t sz = size();//记录扩容前的元素个数if (_start)//如果_start不为空(如果为空也不用复制元素内容了){for (int i = 0; i < sz; i++){tmp[i] = _start[i];//这里如果用memmove或者memcopy对于自定义类型会发生多次析构的问题(相当于浅拷贝),所以还是用赋值,如果自定义类型有实现深拷贝就没问题}delete[] _start;//这里把原来的删除,如果是浅拷贝,这里的删除了,tmp里面的自定义类型指针就变成野指针了}_start = tmp;//修改_start_finish = tmp + sz;//记录sz的好处_endOfStorage = tmp + n;}}void resize(size_t n, const T& value = T())//扩容或减容至n个元素,扩容的话会用value填满所有空间{if (n < size())//判断目标扩容大小是不是小于当前元素个数{T* tmp = new T[n];//直接创建新空间,大小为nfor (int i = 0; i < n; i++){tmp[i] = _start[i];//一个个拷贝,这里用赋值和reserve原因完全一样}//下面也和reserve一样delete[] _start;_start = tmp;_finish = tmp + n;_endOfStorage = tmp + n;}else if (n > size())//这里是目标扩容大小大于当前元素个数{int i = size();//记录当前元素个数reserve(n);//扩容while (i < n){_start[i++] = value;//从当前元素的结尾开始填充,直到容量达到目标扩容大小}_finish = _start + n;//更新_finish}}

        三.[]重载

        []重载需要断言是否越界,以及还要实现const版本。

        T& operator[](size_t pos)//方括号重载{assert(pos < size());return _start[pos];}const T& operator[](size_t pos) const//const方括号重载{assert(pos < size());return _start[pos];}

        四.修改函数

        修改函数需要注意insert的pos迭代器失效问题以及erase的迭代器失效(对于外部的迭代器)。

        void push_back(const T& x)//尾插{if (_finish == _endOfStorage){reserve(capacity() == 0 ? 4 : capacity() * 2);}_start[size()] = x;//结尾插入元素_finish++;//更新_finish//或者复用insert//insert(end(),x);}void pop_back()//尾删{erase(end());//直接调用erase函数尾删}void swap(vector<T>& v)//交换两个vector的成员变量{std::swap(_start, v._start);std::swap(_finish, v._finish);std::swap(_endOfStorage, v._endOfStorage);}iterator insert(iterator pos, const T& x)//在pos位置之前插入{assert(pos >= begin());//断言,防止pos越界assert(pos <= end());size_t sz = pos - _start;//计算并记录pos和_start的相对距离,也是待会要插入的位置if (_finish == _endOfStorage)//判断是否要扩容{reserve(capacity() == 0 ? 4 : capacity() * 2);}//重点注意,这里是insert迭代器可能失效的地方pos = _start + sz;//如果是异地扩容,那么pos所指的位置会被删除就变成野指针了,所以要更新pos位置,之前记录的sz就有用了if (size())//如果当前元素个数不为0的话{//从后往前走,然后把左边的数据挪到右边,需要注意下标的越界问题iterator end = _finish;while (end > pos){*end = *(end - 1);//左边的数据挪到右边end--;}}_start[sz] = x;//放入数据_finish++;//更新_finishreturn pos;//返回插入元素的下一个位置,也就是pos}iterator erase(iterator pos)//删除当前位置的元素{assert(pos >= begin());//判断pos是否越界assert(pos <= end());iterator prev = pos + 1;//记录pos位置的前一个位置//从左往右走,然后把右边的数据挪到左边,直接覆盖要删除的元素while (prev < end()){*(prev - 1) = *prev;//把右边的数据挪到左边prev++;}_finish--;//更新_finish//这里是第二个迭代器失效问题,如果外面的迭代器不接受erase返回的迭代器可能后面的删除会出问题return pos;//返回删除元素的下一个位置(其实还是pos)}

        五.迭代器

         vector迭代器就是原生指针,以及实现const迭代器。

        typedef T* iterator;//vector的迭代器是原生指针,和string一样typedef const T* const_iterator;//const迭代器iterator begin(){return _start;}iterator end(){return _finish;}const_iterator begin() const{return _start;}const_iterator end() const{return _finish;}

        以上就是vector的模拟实现,写完后不仅可以提升对代码的操作熟练度,对于顺序表这种数据结构也能有更深的理解。

相关文章:

C++ vector模拟实现

目录 一.默认成员函数 二.扩容相关函数 三.[]重载 四.修改函数 五.迭代器 继上次写完string之后&#xff0c;可以写一个vector练练手以及熟悉其底层。vector是一个顺序表&#xff0c;相比普通数组不同点在于顺序表的数据必须是连续存放的。 一.默认成员函数 string是只存放字符…...

BUUCTF:[GYCTF2020]FlaskApp

Flask的网站&#xff0c;这里的功能是Base64编码解码&#xff0c;并输出 并且是存在SSTI的 /hint 提示PIN码 既然提示PIN&#xff0c;那应该是开启了Debug模式的&#xff0c;解密栏那里随便输入点什么报错看看&#xff0c;直接报错了&#xff0c;并且该Flask开启了Debug模式&am…...

好玩的调度技术

好玩的调度技术 文章目录 好玩的调度技术前言一、乱金柝-空间剥离二、拖拽编辑三、全端兼容 前言 最近感觉自己抑郁了&#xff0c;生态技术实在太庞大太复杂&#xff0c;所以我决定先停一段时间&#xff0c;在停下写生态的这两天写了几个调度的小玩意换换脑子&#xff0c;很有…...

Android 自定义加解密播放音视频(m3u8独立加密)

文章目录 背景加密流程音视频解密音视频播放结语 背景 当涉及App内部视频的时候&#xff0c;我们不希望被别人以抓包的形式来爬取我们的视频大视频文件以文件方式整个加密的话需要完全下载后才能进行解密当前m3u8格式虽然支持加密&#xff0c;但是ts格式的小视频可以独立播放的…...

常见的文件格式

一、C:\fakepath\新建文本文档.txt [object String] 实现方式&#xff1a; <input onchange"test(this.value)" type"file"></input><script>function test(e){console.log(e,Object.prototype.toString.call(e))}</script> 二、…...

浏览器输入url后回车展开过程

当你在浏览器中输入一个URL并敲下回车后&#xff0c;浏览器会执行一系列步骤来访问并展示网页。下面是浏览器访问网页的一般流程&#xff1a; DNS解析&#xff1a;浏览器首先会提取URL中的主机名&#xff0c;然后向DNS服务器发送请求&#xff0c;将主机名解析为对应的IP地址。这…...

Docker 容器创建命令说明

使用命令如下: docker run -itd --name=cluster -v /home/ClusterApp/cluster:/home/ClusterApp/cluster --restart=always --privileged=true -p 12000:12000 python:3.8 命令说明: docker run 创建一个容器并运行 docker run --help 可以查看所有的参数: 命令中参数说明 -…...

西瓜书读书笔记整理(六)—— 第六章 支持向量机

第六章 支持向量机 6.1 间隔与支持向量6.1.1 什么是支持向量机6.1.2 支持向量与间隔6.1.3 支持向量机的求解过程 6.2 对偶问题&#xff08;dual problem&#xff09;6.2.1 什么是对偶问题6.2.2 如何求解支持向量机的对偶问题 6.3 核函数&#xff08;kernel function&#xff09…...

蓝桥杯每日一题2023.9.23

4961. 整数删除 - AcWing题库 题目描述 分析 注&#xff1a;如果要进行大量的删除操作可以使用链表 动态求最小值使用堆&#xff0c;每次从堆中取出最小值的下标然后在链表中删除 注意long long 代码解释&#xff1a; while(k --){auto t q.top();q.pop();res t.first;i…...

C语言数组和指针笔试题(三)(一定要看)

目录 字符数组四例题1例题2例题3例题4例题5例题6例题7 结果字符数组五例题1例题2例题3例题4例题5例题6例题7结果字符数组六例题1例题2例题3例题4例题5例题6例题7 结果 感谢各位大佬对我的支持,如果我的文章对你有用,欢迎点击以下链接 &#x1f412;&#x1f412;&#x1f412;个…...

leetcode11 盛水最多的容器

题目 给定一个长度为 n 的整数数组 height 。有 n 条垂线&#xff0c;第 i 条线的两个端点是 (i, 0) 和 (i, height[i]) 找出其中的两条线&#xff0c;使得它们与 x 轴共同构成的容器可以容纳最多的水。 返回容器可以储存的最大水量。 示例 输入&#xff1a;[1,8,6,2,5,4,8,…...

进入数据结构的世界

数据结构和算法的概述 一、什么是数据结构二、什么是算法三、如何去学习数据结构和算法四、算法的时间复杂度和空间复杂度4.1 算法效率4.2 大O的渐进表示法4.3 时间复杂度4.4 空间复杂度4.5 常见复杂度对比 一、什么是数据结构 数据结构是计算机存储、组织数据的方式。&#x…...

stm32之看门狗

STM32 有两个看门狗&#xff0c;独立看门狗和窗口看门狗&#xff0c;独立看门狗又称宠物狗&#xff0c;窗 口看门狗又称警犬。可用来检测和解决由软件错误引起的故障。两个看门狗的原理都是当计数器达到给定的超时值时&#xff0c;产生系统复位&#xff0c;对于窗口型看门狗同…...

纤维蛋白单体(FM)介绍

纤维蛋白单体&#xff08;FM&#xff09;是血栓形成的早期产物&#xff0c;是纤维蛋白原&#xff08;Fibrinogen&#xff0c;Fbg&#xff09;在凝血酶&#xff08;Thrombin&#xff09;的作用下&#xff0c;脱掉肽A&#xff08;Fibrinopeptide A&#xff0c;Fp A&#xff09;与…...

知识图谱实战导论:从什么是KG到LLM与KG/DB的结合实战

前言 本文侧重讲解&#xff1a; 什么是知识图谱LLM与langchain/数据库/知识图谱的结合应用 比如&#xff0c;虽说基于知识图谱的问答 早在2019年之前就有很多研究了&#xff0c;但谁会想到今年KBQA因为LLM如此突飞猛进呢 第一部分 知识图谱入门导论 //待更.. 第二部分 LLM与…...

第5章 会话与会话技术

第5章 会话与会话技术 一. 单选题&#xff08;共5题&#xff0c;50分&#xff09;二. 判断题&#xff08;共5题&#xff0c;50分&#xff09; 一. 单选题&#xff08;共5题&#xff0c;50分&#xff09; (单选题) 阅读下面代码&#xff1a; Book book BookDB.getBook(id)…...

IDEA2023新UI回退老UI

idea2023年发布了新UI&#xff0c;如下所示 但是用起来真心不好用&#xff0c;各种位置也是错乱&#xff0c;用下面方法可以回退老UI...

ElasticSearch(三)

1.数据聚合 聚合&#xff08;aggregations&#xff09;可以让我们极其方便的实现对数据的统计、分析、运算。例如&#xff1a; 什么品牌的手机最受欢迎&#xff1f; 这些手机的平均价格、最高价格、最低价格&#xff1f; 这些手机每月的销售情况如何&#xff1f; 实现这些…...

【LinkedHashMap】146. LRU 缓存

146. LRU 缓存 解题思路 与普通的 HashMap 不同&#xff0c;LinkedHashMap 会保持元素的有序性。这可以在某些情况下提供更可预测的迭代顺序直接获取元素 因为使用到该元素 将该元素重新放入队尾 表示最近使用该元素写入元素&#xff0c;首先如果该元素原来存在 那么需要将ke…...

Opencv-python去图标与水印方案实践

RGB色彩模式是工业界的一种颜色标准&#xff0c;是通过对红&#xff08;R&#xff09;、绿&#xff08;G&#xff09;、蓝&#xff08;B&#xff09;三个颜色通道的变化以及它们相互之间的叠加来得到各式各样的颜色的&#xff0c;RGB即是代表红、绿、蓝三个通道的颜色&#xff…...

基于算法竞赛的c++编程(28)结构体的进阶应用

结构体的嵌套与复杂数据组织 在C中&#xff0c;结构体可以嵌套使用&#xff0c;形成更复杂的数据结构。例如&#xff0c;可以通过嵌套结构体描述多层级数据关系&#xff1a; struct Address {string city;string street;int zipCode; };struct Employee {string name;int id;…...

Lombok 的 @Data 注解失效,未生成 getter/setter 方法引发的HTTP 406 错误

HTTP 状态码 406 (Not Acceptable) 和 500 (Internal Server Error) 是两类完全不同的错误&#xff0c;它们的含义、原因和解决方法都有显著区别。以下是详细对比&#xff1a; 1. HTTP 406 (Not Acceptable) 含义&#xff1a; 客户端请求的内容类型与服务器支持的内容类型不匹…...

SCAU期末笔记 - 数据分析与数据挖掘题库解析

这门怎么题库答案不全啊日 来简单学一下子来 一、选择题&#xff08;可多选&#xff09; 将原始数据进行集成、变换、维度规约、数值规约是在以下哪个步骤的任务?(C) A. 频繁模式挖掘 B.分类和预测 C.数据预处理 D.数据流挖掘 A. 频繁模式挖掘&#xff1a;专注于发现数据中…...

【解密LSTM、GRU如何解决传统RNN梯度消失问题】

解密LSTM与GRU&#xff1a;如何让RNN变得更聪明&#xff1f; 在深度学习的世界里&#xff0c;循环神经网络&#xff08;RNN&#xff09;以其卓越的序列数据处理能力广泛应用于自然语言处理、时间序列预测等领域。然而&#xff0c;传统RNN存在的一个严重问题——梯度消失&#…...

376. Wiggle Subsequence

376. Wiggle Subsequence 代码 class Solution { public:int wiggleMaxLength(vector<int>& nums) {int n nums.size();int res 1;int prediff 0;int curdiff 0;for(int i 0;i < n-1;i){curdiff nums[i1] - nums[i];if( (prediff > 0 && curdif…...

家政维修平台实战20:权限设计

目录 1 获取工人信息2 搭建工人入口3 权限判断总结 目前我们已经搭建好了基础的用户体系&#xff0c;主要是分成几个表&#xff0c;用户表我们是记录用户的基础信息&#xff0c;包括手机、昵称、头像。而工人和员工各有各的表。那么就有一个问题&#xff0c;不同的角色&#xf…...

VTK如何让部分单位不可见

最近遇到一个需求&#xff0c;需要让一个vtkDataSet中的部分单元不可见&#xff0c;查阅了一些资料大概有以下几种方式 1.通过颜色映射表来进行&#xff0c;是最正规的做法 vtkNew<vtkLookupTable> lut; //值为0不显示&#xff0c;主要是最后一个参数&#xff0c;透明度…...

C++.OpenGL (10/64)基础光照(Basic Lighting)

基础光照(Basic Lighting) 冯氏光照模型(Phong Lighting Model) #mermaid-svg-GLdskXwWINxNGHso {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-GLdskXwWINxNGHso .error-icon{fill:#552222;}#mermaid-svg-GLd…...

EtherNet/IP转DeviceNet协议网关详解

一&#xff0c;设备主要功能 疆鸿智能JH-DVN-EIP本产品是自主研发的一款EtherNet/IP从站功能的通讯网关。该产品主要功能是连接DeviceNet总线和EtherNet/IP网络&#xff0c;本网关连接到EtherNet/IP总线中做为从站使用&#xff0c;连接到DeviceNet总线中做为从站使用。 在自动…...

【HarmonyOS 5 开发速记】如何获取用户信息(头像/昵称/手机号)

1.获取 authorizationCode&#xff1a; 2.利用 authorizationCode 获取 accessToken&#xff1a;文档中心 3.获取手机&#xff1a;文档中心 4.获取昵称头像&#xff1a;文档中心 首先创建 request 若要获取手机号&#xff0c;scope必填 phone&#xff0c;permissions 必填 …...