vector模拟实现

vector的模拟实现

  • 一. vector的模拟实现
    • 1.0 与string的区别
    • 1.1 实现内容
    • 实现方法
  • 二. vector模拟中重点讲解内容(坑)
    • 2.1 erase的使用问题
    • 2.2 resize的特殊写法
    • 2.3 operator =
    • 2.4 reserve
  • 三. 整体代码

一. vector的模拟实现

我们知道:
在STL中,我们上次学了string
但其实准确来说string并不算是STL中的成员。
因为string出现的比STL要早
所以string的设计相比于STL中其他的容器要麻烦的多。

但是今天带来的vector可以说是是完完全全的STL中的容器。

这里可能还有人不知道容器是什么东西。
容器准确来说是用来处理数据的
就比如我们之前学习进程的时候,在管理文件的时候
文件:文件内容+文件属性
本质都是数据,所以我们能看到很多的链表和顺序表的成分在

而之后我们要学习的算法
作用则是:处理数据
存储数据+处理数据可以说是组成了程序

1.0 与string的区别

为什么这里要讲string和vector的区别?

因为string和vector一样实际上就是一个顺序表

但是还是有很大的区别:

而vector通过模板的功能来实现,能存储多种类型的数据

string相当于将char类型的数据拎出来,为它拓宽了很多接口的同时,为了兼容C语言,保留了很多字符串的特殊点。
所以:
i. string只能存储char类型
ii. string保留了末尾是’\0’的成分

而vector则是全新的对象,不需要兼容C语言。
i. 所以vector能存储各种类型
ii. 同时末尾不用添加’\0’

1.1 实现内容

我们模拟实现vector的目的:
是为了更好的了解vector来帮助我们更好的使用和学习。

所以这里就挑几个比较重要的接口进行实现
做到一个比较基础的vector。

在这里插入图片描述
这个就是大致的实现内容。

因为这个和string的实现实在是太像了,所以就选几个比较重点的讲

其他未讲到的会后面会放在整体代码中。

实现方法

这里不同于string的capacity和data实现方法

这里vector用的是三个指针成员变量的实现方法,但是实际上没有相差多少,用string的同样可以实现。

class vector 
{
public:
private:iterator _start;iterator _finish;iterator _eofstorage;
};

这里的:
_start:就是开辟数组的开始指针
_finishi:就是开辟数组最后一个元素的指针
_eofstorage:数组开辟空间的空间的最后位置的地址

这个实现方法区别其实不重要,用string的实现方法依旧可以实现。

二. vector模拟中重点讲解内容(坑)

2.1 erase的使用问题

erase
这个函数的功能是:
删除指定位置的元素。

int main()
{std::vector<int> v1;v1.push_back(1);v1.push_back(2);v1.push_back(3);v1.push_back(4);std::vector<int>::iterator begin = v1.begin();v1.erase(begin);for (auto i : v1){std::cout << i;}
}

在这里插入图片描述
具体就是这样使用。

接下来就来演示erase的一个非常经典的错误用法。

这里用的是STL中的vector,因为官方的使用中,这个问题也是十分常见的。

#include<iostream>
#include<vector>
#include"vector.h"
int main()
{std::vector<int> v1;v1.push_back(1);v1.push_back(2);v1.push_back(3);v1.push_back(4);std::vector<int>::iterator begin = v1.begin();v1.erase(begin);while (begin != v1.end()){std::cout << *begin;begin++;}
}

在这里插入图片描述
这里能发现程序崩掉了。

这是我们发现
这里我们把begin迭代器指定的内容删除后
然后继续使用了begin迭代器。

但是按照我们的道理来讲
这个begin的指向的方向应该还是老方向。
erase之后,各类元素会向前面补位,begin应该指向新补位的2了。

按道理来说这个begin应该是可以继续使用的。

因为在vs中,这个问题算是迭代器失效
在使用迭代器对象进行insert与erase之后。
vs会默认该迭代器无法继续使用

我们知道STL不同编译器下是不同的,因为官方给了一个STL的最基本的设定,具体的修改和实现方法是取决于公司自己

所以在linux中,可能就不会判断这个迭代器失效的效果。

这里我们来试一下。

在这里插入图片描述
还是同样的代码
在这里插入图片描述

这里能发现在linux中能进行访问。

但是这样的话为什么vs要禁止用户使用呢?
这里就要讲下允许使用这个的坏处了。

这里我们在linux中用这个测试一道题:
删除数组中的所有偶数
在这里插入图片描述

在这里插入图片描述
这里结果我们发现少删除了一个2。
这里其实仔细观察一下我们就能发现问题所在。

在这里插入图片描述
这里就能发现it删除完,还会进行一次++,会略过连续的偶数。

这里掠过算是比较ok一点的错误了,但是如果想象一下最后一位是偶数,这样的话会直接掠过数组的最后一位,直接向后面遍历,非常容易导致越界访问

这里其实要解决也是很简单。

在这里插入图片描述
只要在其中加一个else就可以了。
在这里插入图片描述

但是这样虽然能解决问题,但是大大限制了它的平台可移植性。

所以这个情况也是开发者所在意的。

在这里插入图片描述
这里能发现erase有一个iterator 的返回值。
这个iterator是用来返回下一个位置的值的。
在这里插入图片描述
所以这里需要去接受一下返回值,才能继续用这个begin变量。

2.2 resize的特殊写法

void resize(int n, const T& val = T())
{if (n < size()){_finish = _start + n;}else{reserve(n);while (_finish != _start + n){*_finish = val;_finish++;}}
}

这个是resize的函数实现。
我们知道:
reserve:开辟空间,插入数据时不扩容
resize:开空间+初始化为指定值

但是resize初始化为指定值的时候,指定值有多种不同的类型。

但是有时候如果用户不输入初始化的值该怎么办

void resize(int n, const T& val = T())

这个第二个参数,const T& val=T();
这个可以说是一个新写法,这个前面是类型

val=T();是缺省值。
那这个T();
其实就是调用T类型的构造函数的的意思。

那这样我们大致就能明白是什么意思了
如果用户没有给初始化值,那就去调用vector类型T的构造函数

2.3 operator =

这里的operator和string的的一样,要注意深浅拷贝的问题。

所以同样可以用到swap深拷贝函数。

void check_capacity(){if (_finish == _eofstorage){if (_start == NULL)reserve(4);elsereserve(capacity() * 2);}}

2.4 reserve

void reserve(int n)
{if (n > capacity()){int oldsize = capacity();T* new_vec = new T[n];if (_start != nullptr){for(int i=0;i<oldsize;i++){new_vec[i]=_start[i];}delete _start;}_start = new_vec;_finish = _start + oldsize;_eofstorage = _start + n;}
}

这个是完成版的reserve

		void reserve(int n){if (n > capacity()){int oldsize = capacity();T* new_vec = new T[n];if (_start != nullptr){memcpy(new_vec, _start, oldsize*sizeof(T));delete _start;}_start = new_vec;_finish = _start + oldsize;_eofstorage = _start + n;}}

这个是改版前的reserve函数

这里发现只有一个地方发生了了改变。

if (_start != nullptr)
{memcpy(new_vec, _start, oldsize*sizeof(T));delete _start;
}

这里乍一看好像没有什么问题。

但是我们仔细想想,如果vector的类型是一个自定义类型,具有地址成员变量。

那memcpy对new_vec与_start进行改变时,就会这个是浅拷贝,无法进行深拷贝,就会发生严重的问题

所以就改成了

for(int i=0;i<oldsize;i++){new_vec[i]=_start[i];}

这里使用赋值重载来进行深度拷贝。
就没有这样的问题了

三. 整体代码

#include<string.h>
#include<assert.h>
#include<iostream>
namespace my_vector 
{template<typename T>class vector {public:typedef T* iterator;typedef const T* const_iterator;vector():_start(nullptr),_finish(nullptr),_eofstorage(nullptr){}vector(const vector<T>& v){_eofstorage=_finish=_start=nullptr;for(int i=0;i<v.size();i++){push_back(v[i]);}}vector(int n,const T& data){_start=nullptr;_finish=nullptr;_eofstorage=nullptr;resize(n,data);}template<class alliterator>vector(alliterator first,alliterator end){_finish=nullptr;_start=nullptr;_eofstorage=nullptr;while(first!=end){push_back(*first);first++;} }~vector(){delete _start;_start = _finish =_eofstorage = nullptr;}void check_capacity(){if (_finish == _eofstorage){if (_start == NULL)reserve(4);elsereserve(capacity() * 2);}}vector<T>&  operator= (const vector<T>& v){if(this!=v){ std::swap(_start,v._start);std::swap(_finish,v.finish);std::swap(_eofstorage,v._eofstorage);}return *this;}void push_back(const T& data){check_capacity();insert(size(), data);}void reserve(int n){if (n > capacity()){int oldsize = capacity();T* new_vec = new T[n];if (_start != nullptr){for(int i=0;i<oldsize;i++){new_vec[i]=_start[i];}delete _start;}_start = new_vec;_finish = _start + oldsize;_eofstorage = _start + n;}}iterator begin(){return _start;}iterator end(){return _finish;}size_t size() const{return _finish - _start;}void insert(int pos,const T& data){assert(pos >= 0 && pos <= size());check_capacity();T* end = _finish;while (end > _start+pos){*end = *(end - 1);end--;}_start[pos] = data;_finish++;}void erase(int pos){assert(pos >=0 && pos <= size());T* end = _start+pos;while (end <_finish){*end = *(end + 1);end++;}_finish--;}size_t capacity() const{return _eofstorage - _start;}T& operator[](int pos){assert(pos < size());return _start[pos];}const T& operator[](int pos) const{assert(pos < size());return _start[pos];}void pop_back(){erase(_finish - _start);}const_iterator begin() const{return _start;}const_iterator end() const{return _finish;}void resize(int n,const T& val=T()){if (n < size()){_finish=_start + n;}else{reserve(n);while(_finish!=_start+n){*_finish=val;_finish++;}}}private:iterator _start;iterator _finish;iterator _eofstorage;};
}

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

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

相关文章

iPhoto照片垃圾清理工具CleanMyMacX2024

CleanMyMacX的照片垃圾清理功能会浏览您的iPhoto图片库&#xff0c;找到那些经过裁剪、调整大小、旋转或者其他编辑之后不再需要的但仍被iPhoto保存的原始图片副本。这些副本是隐藏的&#xff0c;但是它们却极大的增加了图片库的大小。 CleanMyMac X全新版下载如下: https://…

揭秘ChatGPT的智慧密码:向量数据库的神奇作用解析

2023年8月&#xff0c;亚马逊云科技推出了自己的向量引擎Amazon OpenSearch Serverless向量引擎&#xff0c;这被认为是其旗下大语言模型Amazon Titan超越ChatGPT的一个重要契机。 那么&#xff0c;这个Amazon OpenSearch Serverless向量引擎有何厉害之处&#xff1f;为什么能…

好用的MybatisX插件~

MybatisX插件&#xff1a; MyBatis-Plus为我们提供了强大的mapper和service模板&#xff0c;能够大大的提高开发效率。但是在真正开发过程中&#xff0c;MyBatis-Plus并不能为我们解决所有问题&#xff0c;例如一些复杂的SQL&#xff0c;多表联查&#xff0c;我们就需要自己去…

二十、泛型(1)

本章概要 基本概念 与 C 的比较 简单泛型 一个元组类库一个堆栈类RandomList 基本概念 普通的类和方法只能使用特定的类型&#xff1a;基本数据类型或类类型。如果编写的代码需要应用于多种类型&#xff0c;这种严苛的限制对代码的束缚就会很大。 多态是一种面向对象思想的泛…

2-爬虫-代理池搭建、代理池使用(搭建django后端测试)、爬取某视频网站、爬取某视频网站、bs4介绍和遍历文档树

1 代理池搭建 2 代理池使用 2.1 搭建django后端测试 3 爬取某视频网站 4爬取某视频网站 5 bs4介绍和遍历文档树 1 代理池搭建 # ip代理-每个设备都会有自己的IP地址-电脑有ip地址---》访问一个网站---》访问太频繁---》封ip-收费&#xff1a;靠谱稳定--提供api-免费&#xff…

Framebuffer 介绍和应用编程

前言&#xff1a; 使用的开发板为韦东山老师的 IMX6ULL 目录 Framebuffer介绍 LCD 操作原理 涉及的 API 函数 1.open 函数 2.ioctl 函数 3.mmap 函数 Framebuffer 程序分析 1.打开设备 2.获取 LCD 参数 3.映射 Framebuffer 4.描点函数 5.随便画几个点 6.上机实验…

RPC 原理详解

文章目录 什么是 RPCRPC 基本原理RPC核心功能服务寻址数据编解码网络传输一次RPC的调用过程 实践基于HTTP协议的RPC基于TCP协议的RPC 什么是 RPC RPC&#xff08;Remote Procedure Call&#xff09;&#xff0c;即远程过程调用&#xff0c;它允许像调用本地服务一样调用远程服…

DC电源模块隔离电路的影响

BOSHIDA DC电源模块隔离电路的影响 DC电源模块隔离电路是电子设备中常用的一种电路。它的作用是在设备中两个电路之间建立一定的隔离&#xff0c;以保证两个电路之间不会传递电流或信号。这种隔离电路的影响可以从以下几个方面来分析。 首先&#xff0c;隔离电路可以提高安全性…

Linux个性化登录提示信息

在Linux系统中&#xff0c;您可以为每个用户登录后显示个性化的提示信息。这通常通过修改用户的shell配置文件来实现&#xff0c;这个文件通常是用户的.bashrc或.bash_profile文件。以下是一些示例步骤&#xff0c;来实现这个目标&#xff1a; 打开终端并登录到Linux系统。 使…

第68讲:MySQL触发器的核心概念以及常见的触发类型应用案例

文章目录 1.触发器的概念2.触发器操作的语法结构3.各类触发器的典型应用案例3.1.需求描述以及实现思路3.2.创建日志表3.3.INSERT类型的触发器3.4.UPDATE类型的触发器3.5.DELETE类型的触发器 1.触发器的概念 触发器是与表中数据相关的数据库对象&#xff0c;当表中的数据产生in…

完美解决RuntimeError: expected scalar type Long but found Float

文章目录 一、错误解释RuntimeError: expected scalar type Long but found Float二、错误分析三、解决办法总结 一、错误解释RuntimeError: expected scalar type Long but found Float RuntimeError&#xff1a;应为标量类型Long&#xff0c;但找到了Float 二、错误分析 我…

MongDB 的安装 无废话

MongDB 的安装 1 安装 MongDB https://www.mongodb.com/try/download/community-kubernetes-operator 这里我们选择 ZIP 解压到文件夹 创建 data 文件 在 data 文件夹里面创建 db 和 logs 文件夹 进入 bin 目录 输入 cmd 回车 2 启动 MongDB 输入启动命令 mongod --dbpath..\…

csapp bomb lab part4

csapp bomb lab part4 phase 5 是一个循环&#xff0c;不断累加&#xff0c;访问的地址基于0x4024b0, phase 6 之后更新 汇编 地址计算 寄存器 cl 是 x86 汇编语言中的一个 8 位寄存器&#xff0c;它是 ecx 寄存器的低位部分。 具体来说&#xff0c;x86 架构中的寄存器可…

centos7中多版本go安装

安装go的方式 官网下载tar.gz包安装 # 1.下载tar包 wget https://go.dev/dl/go1.18.1.linux-amd64.tar.gz # 2.解压tar包到指定路径 tar -xvf go1.18.1.linux-amd64.tar.gz -C /usr/local/go1.18 # 3.配置环境变量&#xff0c;打开 /etc/profile 文件添加以下文件每次开机时…

详解 SpringMVC 的 HttpMessageConverter

文章目录 前言参考资料内容1、[RequestBody ](/RequestBody )2、RequestEntity3、[ResponseBody ](/ResponseBody )4、SpringMVC处理json5、SpringMVC处理ajax6、RestController注解7、ResponseEntity 推荐读物 《Spring Boot微服务实战(第2版)》内容简介目录 前言 HttpMessag…

笔记本360wifi,wifi共享大师掉线,

笔记本上搭建的wifi热点&#xff0c;例如360wifi、wifi共享大师等&#xff0c;手机连接wifi后总是隔一段时间掉线。原因:网卡驱动和无线驱动有问题&#xff0c;需要更新或换成稳定的网卡驱动和无线驱动。 解决方案: ① 安装驱动精灵: ②点击驱动管理 ③ ④ ⑤进行阻止windo…

iOS自动化测试方案(四):保姆级搭建iOS自动化开发环境

文章目录 一、基础环境准备1.1、MacOS虚拟机 二、iPhone虚拟机三、MacOS虚拟机连接iphone真机四、扩展&&问题4.1、如果appium启动app失败&#xff0c;可能是appium driver没有安装xcuitest插件4.2、下载并安装Appium服务端&#xff0c;inspector元素定位器4.3、下载app…

贝锐向日葵亮相阿里云“云栖大会”:独创专利算法赋能全新云桌面

2023年10月31日-11月2日&#xff0c;一年一度的云栖大会如期举办&#xff0c;国产远程连接服务创领者贝锐受邀参与。活动现场&#xff0c;贝锐CTO张小峰进行了分享&#xff0c;宣布贝锐旗下国民级远程控制品牌“贝锐向日葵”与无影展开合作&#xff0c;同时全新的“云桌面”将于…

数据结构:AVL树讲解(C++)

AVL树 1.AVL树的概念2.平衡因子3.节点的定义4.插入操作5.旋转操作&#xff08;重点&#xff09;5.1左单旋5.2右单旋5.3左右双旋5.4右左双旋 6.一些简单的测试接口7.完整代码 1.AVL树的概念 普通二叉搜索树&#xff1a;二叉搜索树 二叉搜索树虽可以缩短查找的效率&#xff0c;但…

安装MySQL时出现 由于找不到 MSVCR120.dll,无法继续执行代码。重新安装程序可能会解决此问题。

--------------------------- mysqld.exe - 系统错误 --------------------------- 由于找不到 MSVCR120.dll&#xff0c;无法继续执行代码。重新安装程序可能会解决此问题。 --------------------------- 确定 --------------------------- 安装MySQL时出现 “This appl…