C++ vector模拟实现

目录

    • 使用insert时迭代器失效
    • 使用erase时迭代器失效
    • 使用memcpy浅拷贝的问题
    • 调用最匹配的函数可能出现的问题
    • 模拟实现vector

使用insert时迭代器失效

在模拟vector插入的时候会遇到扩容后pos失效的问题,需要更新pos

在这里插入图片描述

vector():_start(nullptr), _finish(nullptr), _endofstorage(nullptr){}void insert(iterator pos, const T& value = T()){assert(pos >= _start && pos <= _finish);if (size() == capacity()){//pos位置的空间可能在扩容的时候被delete,要记录相对位置更新posint len = pos - _start;reserve(capacity() == 0 ? 4 : 2 * capacity());pos = _start + len;}iterator end = _finish - 1;while (end >= pos){*(end + 1) = *end;end--;}*pos = value;_finish++;}
void test2(){vector<int> v;v.insert(v.begin(), 1);vector<int>::iterator it = v.begin();v.insert(it, 2);v.insert(it, 3);v.insert(it, 4);v.insert(it, 5);//这句代码之后it就失效了v.insert(it, 6);//报错,因为it传给pos的位置已经被销毁了}

insert插入后it就失效了,我们不知道什么时候扩容
给pos加引用的话,v.insert(v.begin(), 1);是不行的,因为begin返回的是临时变量的拷贝不可以修改
如果再给pos加const,那pos不能更改了

所以insert之后it失效,尽量不要再使用

库里的insert会返回新插入元素所在位置的迭代器

void test_vector9(){std::vector<int> v3;v3.push_back(10);v3.push_back(20);v3.push_back(30);v3.push_back(40);for (auto e : v3){cout << e << " ";//结果是10 20 30 40}cout << endl;std::vector<int>::iterator it = v3.begin()+3;int n = 6;while (n--){it = v3.insert(it, n);}for (auto e : v3){cout << e << " ";//结果是10 20 30 0 1 2 3 4 5 40}cout << endl;}

使用erase时迭代器失效

iterator erase(iterator pos)//返回被删位置的下一个位置{assert(pos >= _start && pos <= _finish);iterator it = pos + 1;while (it < _finish){*(it - 1) = *it;it++;}_finish--;return pos;}void test4(){// 1 2 3 4 5// 1 2 3 4 5 6// 2 2 3 4 5vector<int> v;v.push_back(1);v.push_back(2);v.push_back(3);v.push_back(4);v.push_back(5);v.push_back(6);for (auto e : v){cout << e << " ";}cout << endl;auto it = v.begin();while (it != v.end()){if (*it % 2 == 0){v.erase(it);}++it;}for (auto e : v){cout << e << " ";}cout << endl;}

不仅会漏检查,删最后一个的时候会出现 it 比_finish大的情况
在这里插入图片描述
在这里插入图片描述
vs2019会进行强制检查,erase以后认为it失效了,不能访问,我们模拟实现的跟g++下运行结果一样

erase返回被删位置的下一个位置

void test5(){//std::vector<int> v;vector<int> v;v.push_back(1);v.push_back(2);v.push_back(3);v.push_back(4);v.push_back(4);v.push_back(5);v.push_back(6);auto it = v.begin();while (it != v.end()){if (*it % 2 == 0){it = v.erase(it);}else{++it;}}for (auto e : v){cout << e << " ";}cout << endl;}

使用memcpy浅拷贝的问题

模拟实现的vector中的reserve接口中,使用memcpy进行的拷贝会出现问题

memcpy会将一段内存空间中内容拷贝到另外一段内存空间中,这种拷贝是浅拷贝,如果T是内置类型不会有问题,但如果是自定义类型就会出错
在这里插入图片描述

delete的时候如果是自定义的数据类型,会去调用这个对象的析构函数,释放的空间置成随机值,查编码表之后可能就是我们不认识的字

tmp中string对象的_str和_start中string对象的_str是一样的,所以_start中string对象的_str变了,tmp中的也会变

在这里插入图片描述

调string的赋值的时候,是深拷贝,就可以解决问题了,引用计数的浅拷贝也可以解决
库里面实现是拷贝构造的,因为库里的T[]是内存池来的(就像malloc来的没有初始化),要初始化就用定位new调拷贝构造。

所以自定义类型拷贝数据不能用memcpy

在这里插入图片描述
如果不加也可以过,但是推荐加上,因为类名不是类型

调用最匹配的函数可能出现的问题

在这里插入图片描述
出现编译错误的原因是:

会找最匹配的去调用
上面的是int和int
下面的是unsigned int 和int
上面的更匹配所以会调用模板

如果都是int,那么有更匹配的就不会去实例化模板:

vector(int n, const T& val = T())
{reserve(n);for (int i = 0; i < n; i++){push_back(val);}
}

模拟实现vector

#include<assert.h>
#include<iostream>
using namespace std;namespace vc
{template<class T>class vector{public:typedef T* iterator;typedef const T* const_iterator;iterator begin(){return _start;}iterator end(){return _finish;}const iterator begin()const{return _start;}const iterator end()const{return _finish;}vector():_start(nullptr), _finish(nullptr), _endofstorage(nullptr){}template <class InputIterator>vector(InputIterator first, InputIterator last){while (first != last){push_back(*first);first++;}}vector(size_t n, const T& value = T()){reserve(n);while (n--){push_back(value);}}vector(int n, const T& value = T()){reserve(n);while (n--){push_back(value);}}vector(const vector<T>& v)//拷贝构造:_start(nullptr), _finish(nullptr), _endofstorage(nullptr){reserve(v.capacity());for (auto e : v){push_back(e);}}vector<T>& operator=(vector<T> tmp){swap(tmp);return *this;}~vector(){delete[] _start;_start = _finish = _endofstorage = nullptr;}T& operator[](size_t pos){assert(pos < size());return *(_start + pos);}const T& operator[](size_t pos)const{assert(pos < size());return *(_start + pos);}void swap(vector<T>& v){std::swap(_start, v._start);std::swap(_finish, v._finish);std::swap(_endofstorage, v._endofstorage);}size_t size() const{return _finish - _start;}size_t capacity() const{return _endofstorage - _start;}void resize(size_t n, const T& value = T())
//调用默认构造函数然后拷贝构造给value,匿名对象具有常性要加const,
//const会延长生命周期,内置类型也可以认为有构造函数{if (n < size()){_finish = _start + n;}else{reserve(n);while (_finish < _start + n){*_finish = value;_finish++;}}}void reserve(size_t n){if (n > capacity()){size_t sz = size();T* tmp = new T[n];if (_start){//memcpy(tmp, _start,sizeof(T)*sz);//memcpy是浅拷贝,自定义类型会出错for (size_t i = 0; i < sz; i++){tmp[i] = _start[i];}delete[] _start;}_start = tmp;_finish = tmp + sz;_endofstorage = tmp + n;}}void push_back(const T& x){if (size() == capacity()){reserve(capacity()==0?4:2*capacity());}*_finish = x;_finish++;}void pop_back(){assert(size() > 0);_finish--;}iterator insert(iterator pos, const T& value = T())//返回新插入元素所在位置的迭代器{assert(pos >= _start && pos <= _finish);if (size() == capacity()){//pos位置的空间可能在扩容的时候被delete,要记录相对位置更新posint len = pos - _start;reserve(capacity() == 0 ? 4 : 2 * capacity());pos = _start + len;}iterator end = _finish - 1;while (end >= pos){*(end + 1) = *end;end--;}*pos = value;_finish++;return pos;}iterator erase(iterator pos)//返回被删位置的下一个位置{assert(pos >= _start && pos <= _finish);iterator it = pos + 1;while (it < _finish){*(it - 1) = *it;it++;}_finish--;return pos;}private:iterator _start;iterator _finish;iterator _endofstorage;};
}

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

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

相关文章

解耦只是一个巧合?

本文分享一篇在IJCAI2023看到的文章&#xff1a;Overlooked Implications of the Reconstruction Loss for VAE Disentanglement 首先回顾下VAE&#xff0c;其loss函数有两项&#xff0c;一项是重构误差&#xff0c;另一项是正则项&#xff1a; L r e c ( x , x ^ ) E q ϕ (…

QT(9.1)对话框与事件处理

作业&#xff1a; 1. 完善登录框 点击登录按钮后&#xff0c;判断账号&#xff08;admin&#xff09;和密码&#xff08;123456&#xff09;是否一致&#xff0c;如果匹配失败&#xff0c;则弹出错误对话框&#xff0c;文本内容“账号密码不匹配&#xff0c;是否重新登录”&…

23062C++QT day2

封装一个结构体&#xff0c;结构体中包含一个私有数组&#xff0c;用来存放学生的成绩&#xff0c;包含一个私有变量&#xff0c;用来记录学生个数&#xff0c; 提供一个公有成员函数&#xff0c;void setNum(int num)用于设置学生个数 提供一个公有成员函数&#xff1a;void…

FPGA实战小项目3

基于FPGA的波形发生器 基于FPGA的波形发生器 基于FPGA的beep音乐播放器设计 基于FPGA的beep音乐播放器设计 基于FPGA的cordic算法实现DDS sin和cosine波形的产生 基于FPGA的cordic算法实现DDS sin和cosine波形的产生

无需公网IP,实现远程访问群晖Drive并挂载为电脑磁盘的方法

文章目录 前言1.群晖Synology Drive套件的安装1.1 安装Synology Drive套件1.2 设置Synology Drive套件1.3 局域网内电脑测试和使用 2.使用cpolar远程访问内网Synology Drive2.1 Cpolar云端设置2.2 Cpolar本地设置2.3 测试和使用 3. 结语 前言 群晖作为专业的数据存储中心&…

利用MQ实现mysql与elasticsearch数据同步

流程 1.声明exchange、queue、RoutingKey 2. 在hotel-admin中进行增删改&#xff08;SQL&#xff09;&#xff0c;完成消息发送 3. 在hotel-demo中完成消息监听&#xff0c;并更新elasticsearch数据 4. 测试同步 1.引入依赖 <!--amqp--> <dependency><groupId&…

欧洲云巨头OVHcloud收购边缘计算专家 gridscale

边缘计算社区近日获悉&#xff0c;欧洲云巨头OVHcloud已进入全面收购德国公司 gridscale 的谈判&#xff0c;该公司是一家专门从事超融合基础设施的软件提供商。 此次战略收购将标志着 OVHcloud 的另一个重要里程碑&#xff0c;使该集团能够显着加速其地理部署&#xff0c;并进…

lv3 嵌入式开发-6 linux shell脚本编程(概念、变量、语句)

1 Shell脚本概述 1.1Shell脚本概述 Shell脚本是利用 shell 的功能所写的一个程序。这个程序是使用纯文本文件&#xff0c;将一些 shell 的语法与命令&#xff08;含外部命令&#xff09;写在里面&#xff0c;搭配正则表达式、管道命令与数据流重定向等功能 1.2Shell脚本编写流…

[JAVA] byte与int的类型转换案例剖析

总结&#xff1a; ①没有byte的字面值&#xff0c;赋值时需要强制转换类型 ②涉及运算&#xff0c;系统自动进行类型升级&#xff0c;由此用final修饰&#xff0c;代表这是一个不会更改值的常量&#xff0c;通过编译 感受&#xff1a;还是用int吧&#xff0c;自动类型转换太复…

Java程序员所需Javascript知识

它是一种脚本语言&#xff0c;可以用来更改页面内容&#xff0c;控制多媒体&#xff0c;制作图像、动画等等 js 代码位置 <script>// js 代码 </script>引入 js 脚本&#xff0c;在js脚本中写js代码 <script src"js脚本路径"></script>注…

滑动窗口实例7(串联所有单词的子串)

题目&#xff1a; 给定一个字符串 s 和一个字符串数组 words。 words 中所有字符串 长度相同。 s 中的 串联子串 是指一个包含 words 中所有字符串以任意顺序排列连接起来的子串。 例如&#xff0c;如果 words ["ab","cd","ef"]&#xff0c;…

OpenCV之形态学操作

形态学操作包含以下操作&#xff1a; 腐蚀 (Erosion)膨胀 (Dilation)开运算 (Opening)闭运算 (Closing)形态梯度 (Morphological Gradient)顶帽 (Top Hat)黑帽(Black Hat) 其中腐蚀和膨胀操作是最基本的操作&#xff0c;其他操作由这两个操作变换而来。 腐蚀 用一个结构元素…

简述视频智能分析EasyCVR视频汇聚平台如何通过“AI+视频融合”技术规避八大特殊作业风险

视频智能分析EasyCVR视频汇聚平台可以根据不同的场景需求&#xff0c;让平台在内网、专网、VPN、广域网、互联网等各种环境下进行音视频的采集、接入与多端分发。在视频能力上&#xff0c;视频云存储平台EasyCVR可实现视频实时直播、云端录像、视频云存储、视频存储磁盘阵列、录…

noetic 怎么下载robotiq_modbus_tcp 从而使用robotiq二指夹爪

1&#xff0c;会科学上网 2&#xff0c; # Modbus TCP rosdep install robotiq_modbus_tcp3&#xff0c;报错 4&#xff0c;去robotiq的功能包里面找两个大爷&#xff08;tmd&#xff09; 分别修改package.xml 本来是python &#xff0c; 然后分别修改成python3 然后参考其他…

aws-msk-托管kafka集群的简单使用(VPC内部访问:无验证和SASL认证)

1.使用控制台创建即可 根据实例类型创建需要至少15分以上&#xff0c;可以提前创建好ec2实例和Secrets Manager,一会会使用到 2. 创建Secrets Manager &#xff08;使用无认证时请跳过&#xff09; 官方文档&#xff1a;https://docs.aws.amazon.com/zh_cn/msk/latest/deve…

Docker实战:docker compose 搭建Rocketmq

1、配置文件准备 1.1、 新建目录&#xff1a;/home/docker/data/rocketmq/conf mkdir /home/docker/data/rocketmq/conf1.2、 在上面目录下新建文件broker.conf文件&#xff0c;内容如下 brokerClusterName DefaultCluster brokerName broker-a brokerId 0 deleteWhen 0…

[react基础]关于v6版本route的变化,以及常见应用模式

该说不说,在做这些之前,你要记得一件事 route不是react或者vue等原本就有的组件!需要你手动下载!然后导入! 原本的框架只是最终挂载到一个html界面上!!! 别再问我为啥你扒下来的代码为啥不好使了! 讨厌 > _< 下载指令去看我另一篇推文 今天复盘了一下自己的实训…

2023年数学建模国赛A 定日镜场的优化设计思路分析

构建以新能源为主体的新型电力系统&#xff0c;是我国实现“碳达峰”“碳中和”目标的一项重要措施。塔式太阳能光热发电是一种低碳环保的新型清洁能源技术[1]。定日镜是塔式太阳能光热发电站&#xff08;以下简称塔式电站&#xff09;收集太阳能的基本组件&#xff0c;其底座由…

论数据库的种类

摘要 数据库是现代信息管理和数据存储的重要工具&#xff0c;几乎在各个领域都有广泛应用。不同类型的数据库适用于不同的应用场景和需求。本文将介绍几种常见的数据库种类&#xff0c;并探讨它们的特点和适用范围。 正文 一、关系型数据库&#xff08;RDBMS&#xff09; 关…

IntelliJ IDEA中那些让你相见恨晚超好用插件

InteIIiJ IDEA 2023.2版本发布了,在2023.2中&#xff0c;官方根据用户的宝贵反馈对新UI做出了大量改进&#xff0c;新UI界面大大减少了干扰&#xff0c;可以让用户更好地专注于代码。相信很多同学都已经迫不及待地尝试了。 安装激活好 Intellij idea 之后&#xff0c;再搭配如下…