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,一经查实,立即删除!

相关文章

第十六章 ObjectScript 翻译表

文章目录 第十六章 ObjectScript 翻译表 介绍表格列表RAWSAMEHTMLJS or JSMLJSON or JSONMLURIURLUTF8XML 其他表 第十六章 ObjectScript 翻译表 IRIS 使用转换表&#xff08;也称为 I/O 表&#xff09;来完成字符转换任务。某些 API 调用&#xff08;以及 $zconvert 函数&…

iPhoto照片垃圾清理工具CleanMyMacX2024

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

淘宝APP原数据如何获取??

随着中国互联网的快速发展&#xff0c;淘宝APP已成为了人们购物必备的应用程序之一。作为中国最大的在线购物平台之一&#xff0c;淘宝APP每天都会生成海量的交易数据&#xff0c;这些数据对分析消费者行为、预测市场趋势和优化商家运营都非常重要。因此&#xff0c;淘宝APP数据…

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

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

小程序和APP的区别|软件定制开发|网站搭建

小程序和APP的区别|软件定制开发|网站搭建 随着移动互联网的快速发展&#xff0c;小程序和APP成为人们日常生活中不可或缺的应用程序。然而&#xff0c;许多用户常常混淆它们之间的区别&#xff0c;本文将就两者进行对比&#xff0c;以帮助读者更好地理解它们的优势和适用场景…

Android 应用工具类

✍️作者简介&#xff1a;沫小北/码农小北&#xff08;专注于Android、Web、TCP/IP等技术方向&#xff09; &#x1f433;博客主页&#xff1a;沫小北/码农小北 开源中国、稀土掘金、51cto博客、博客园、知乎、简书、慕课网、CSDN &#x1f514;如果文章对您有一定的帮助请&…

labelme2coco.py 脚本增强

前言 最近在做一个实例分割的项目&#xff0c;用使用 Segment anything 的脚本先处理一遍图片&#xff0c;然后用labelme人工审核和调整。&#xff08;labelme 新版本已经加入了 Segment anything&#xff0c;可以AI 抠图&#xff0c;可惜用的是onnxruntime&#xff0c;于是我…

好用的MybatisX插件~

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

CentOS 7上创建Python 3虚拟环境

在CentOS 7上创建Python 3虚拟环境可以使用virtualenv包。以下是创建Python 3虚拟环境的步骤&#xff1a; 确保已经安装了Python 3和pip。可以通过在终端中运行以下命令来检查它们是否已安装&#xff1a; python3 --version pip3 --version如果未安装&#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…

一键同步chromedriver版本

ChromeDriver是一个控制Chrome浏览器的驱动程序&#xff0c;它和Selenium一起被广泛用于Web自动化测试。然而&#xff0c;随着Chrome版本的升级&#xff0c;我们需要不断更新ChromeDriver以保持其与Chrome的兼容性。这个过程既费时又繁琐&#xff0c;而且对于非技术人员来说可能…

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;隔离电路可以提高安全性…

rust 泛型和特征

特征 Trait 定义特征 如果不同的类型具有相同的行为&#xff0c;那么我们就可以定义一个特征&#xff0c;然后为这些类型实现该特征。定义特征是把一些方法组合在一起&#xff0c;目的是定义一个实现某些目标所必需的行为的集合。 例如&#xff0c;我们现在有文章 Post 和微…

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 二、错误分析 我…

将GC编程语言引入WebAssembly的新方法

本文讨论了一种名为 WasmGC 的新方法&#xff0c;用于将垃圾收集编程语言有效地引入 WebAssembly。 WasmGC 定义了新的 GC 类型&#xff0c;例如结构和数组&#xff0c;与之前编译为线性内存的方法 (WasmMVP) 相比&#xff0c;它们可以实现更好的优化&#xff1a; 在编译时和…