C++:模拟实现vector以及vector的迭代器失效和拷贝问题

文章目录

  • 实现的功能
  • 模拟实现
  • 迭代器失效
  • 隐含浅拷贝问题

实现的功能

在这里插入图片描述

模拟实现

由于前面实现了string,因此这里实现过程不为重点,重点为关于迭代器失效和拷贝问题

template <class T>
class vector
{
public:typedef T* iterator;typedef const T* const_iterator;// constructorvector():_start(nullptr), _finish(nullptr), _endofstorge(nullptr){}template <class InputIterator>vector(InputIterator first, InputIterator last): _start(nullptr), _finish(nullptr), _endofstorge(nullptr){while (first != last){push_back(*first);first++;}}vector(size_t n, const T& val = T()){reserve(n);for (int i = 0; i < n; i++){push_back(val);}}vector(int n, const T& val = T()){reserve(n);for (int i = 0; i < n; i++){push_back(val);}}vector(vector<T>& v){reserve(v.capacity());for (auto& e : v){push_back(e);}}// destructor~vector(){delete[] _start;_start = _finish = _endofstorge = nullptr;}// operator=vector<T>& operator=(vector<T>& v){//_start = new T[v.capacity()];//_finish = _start + v.size();//_endofstorge = _start + v.capacity();swap(v);return *this;}// capacitysize_t size(){return _finish - _start;}void resize(size_t num, const T& val = T()){if (num < size()){_finish = _start + num;}else{while (_finish != _start + num){push_back(val);_finish++;}}}size_t capacity(){return _endofstorge - _start;}void reserve(size_t num){if (num > capacity()){T* tmp = new T[num];size_t sz = size();if (_start){for (size_t i = 0; i < sz; i++){tmp[i] = _start[i];}delete[] _start;}_start = tmp;_finish = _start + sz;_endofstorge = _start + num;}}bool empty(){if (_start == _finish)return true;return false;}// element accessT& operator[](size_t pos){return *(_start + pos);}// iteratoriterator begin(){return _start;}iterator end(){return _finish;}const_iterator begin() const{return _start;}const_iterator end() const{return _finish;}// modifiervoid push_back(T data){if (_finish == _endofstorge){size_t sz = size();size_t newcapacity = capacity() == 0 ? 4 : capacity() * 2;T* tmp = new T[newcapacity];if (_start){//memcpy(tmp, _start, sizeof(T) * size());for (size_t i = 0; i < sz; i++){tmp[i] = _start[i];}delete[] _start;}_finish = tmp + sz;_endofstorge = tmp + newcapacity;_start = tmp;}assert(_finish);*(_finish) = data;++_finish;}void pop_back(){--_finish;}void insert(iterator pos, const T& x){assert(pos >= _start);assert(pos <= _finish);if (_finish == _endofstorge){size_t len = pos - _start;reserve(capacity() == 0 ? 4 : capacity() * 2);pos = _start + len;}iterator end = _finish - 1;while (end >= pos){*(end + 1) = *end;--end;}*pos = x;++_finish;}iterator erase(iterator pos){assert(pos >= _start);assert(pos < _finish);iterator it = pos + 1;while (it < _finish){*(it - 1) = *it;++it;}--_finish;return pos;}void swap(vector<T>& v){std::swap(_start, v._start);std::swap(_finish, v._finish);std::swap(_endofstorge, v._endofstorge);}private:iterator _start;iterator _finish;iterator _endofstorge;
};

迭代器失效

首先要清楚,什么是迭代器失效,迭代器失效的应用场景是哪里?

这里举一个例子,迭代器可能会在进行insert的过程中失效,具体失效原因:

迭代器本身可以理解成就是一个指针而这个指针指向的空间又是固定的,因此这里当使用指针时,如果原来的空间由于扩容,导致原来空间被销毁,那么这个迭代器所指向的内容其实也就没有任何意义了,这也就是迭代器失效的原因

用下面这个例子来理解会更方便一些:

void test_vector1()
{vector<int> v(5,5);auto it = v.begin();v.insert(it, 10);v.insert(it, 10);v.insert(it, 10);v.insert(it, 10);v.insert(it, 10);for (auto e : v){cout << e << " ";}cout << endl;
}

在这里插入图片描述

这里定义了一个vector,里面有5个数字5,此时_start所指向的空间是一个区域,因此这里使用迭代器就可以找到容器的头部,实现插入是很方便的

在这里插入图片描述

但当插入数据后,_start所指向的空间发生改变了,而这里的迭代器却依旧指向原来的位置,在这种情况下就是经典的迭代器失效的问题,因此在一些编译器下,标准库下的迭代器如果被insert/erase所使用,迭代器是要被强制检查,不可以被使用的

在有些编译器下,不会进行检查,因此就会产生迭代器失效的情况,基于这样的情况,在使用迭代器时,要注意是否有失效的可能

隐含浅拷贝问题

先来看下面代码

	void push_back(T data){if (_finish == _endofstorge){size_t sz = size();size_t newcapacity = capacity() == 0 ? 4 : capacity() * 2;T* tmp = new T[newcapacity];if (_start){//memcpy(tmp, _start, sizeof(T) * size());for (size_t i = 0; i < sz; i++){tmp[i] = _start[i];}delete[] _start;}_finish = tmp + sz;_endofstorge = tmp + newcapacity;_start = tmp;}assert(_finish);*(_finish) = data;++_finish;}

如果你对memcpy是否可以使用存在疑惑,可以看下面的测试函数

void test_vector1()
{vector<string> v;v.push_back("111111111111111111111");v.push_back("111111111111111111111");v.push_back("111111111111111111111");v.push_back("111111111111111111111");v.push_back("111111111111111111111");for (auto e : v){cout << e << " ";}cout << endl;
}

如果使用memcpy的结果是这样的

在这里插入图片描述

出现这样结果的原因也很简单,这是因为memcpy本质上其实是一种浅拷贝,它的工作原理是把每一个字节的内容都进行拷贝,因此这样其实是一种浅拷贝,用下面的机制来解释可以看成:在这里插入图片描述
因此我们采用了改良版的拷贝机制,让这些数据也进行一定程度的拷贝

在这里插入图片描述
这样就可以实现拷贝的效果

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

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

相关文章

非计算机科班如何丝滑转码?

近年来&#xff0c;很多人想要从其他行业跳槽转入计算机领域。非计算机科班如何丝滑转码&#xff1f; 如何规划才能实现转码&#xff1f; 对于非计算机科班的人来说&#xff0c;想要顺利转码成为计算机相关岗位的从业者&#xff0c;需要经过以下几个步骤&#xff1a; 规划转码…

(docker)mysql镜像拉取-创建容器-容器的使用【个人笔记】

【容器的第一次创建】 容器的第一次创建&#xff0c;需要先下载镜像&#xff0c;从 镜像拉取 0、可以搜索镜像的版本 docker search mysql1、先拉取MySQL的镜像&#xff0c;默认拉取最新版&#xff0c;使用下面的命令拉取mysql镜像 docker pull mysql也可以指定mysql的版本…

MySQL 事务原理:事务概述、隔离级别、MVCC

文章目录 一、事务1.1 事务概述1.2 事务控制语句1.3 ACID特性 二、隔离级别2.1 隔离级别的分类2.1.1 读未提交&#xff08;RU&#xff09;2.1.2 读已提交&#xff08;RC&#xff09;2.1.3 可重复读&#xff08;RR&#xff09;2.1.4 串行化 2.2 命令2.3 并发读异常2.3.1 脏读2.3…

Linux 发行版 Debian 12.1 发布

导读在今年 6 月初&#xff0c;Debian 12“bookworm”发布&#xff0c;而日前 Debian 迎来了 12.1 版本&#xff0c;主要修复系统用户创建等多个安全问题。 Debian 是最古老的 GNU / Linux 发行版之一&#xff0c;也是许多其他基于 Linux 的操作系统的基础&#xff0c;包括 Ub…

Redis单机,主从,哨兵,集群四大模式

Redis 单机模式 Redis 单机模式是指 Redis 数据库在单个服务器上以独立的、单一的进程运行的模式。在这种模式下&#xff0c;Redis 不涉及数据分片或集群配置&#xff0c;所有的数据和操作都在一个实例中进行。以下是关于 Redis 单机模式的详细介绍&#xff1a; 单一实例&#…

数据分析两件套ClickHouse+Metabase(二)

Metabase篇 Metabase安装部署 任何问题请查看 -> 官方文档 jar包从GitHub下载 -> 地址 同样有个问题, 默认数据源里没有ClickHouse, 不过ClickHouse官方提供了插件包 -> 插件包 在安装metabase目录下新建一个plugins文件夹, 把下载的clickhouse.metabase-driver.ja…

spark 图计算 助力解决 dataframe中的链式依赖

链式依赖说明 name newName a b c d b c 我们需要的结果 即我们可以支持获取到链式转换的 起点 重点 以及链式的中间转换过程顺序数组. 特别说明: 出版只支持 单向 无分叉的图,其他复杂场景暂时未测试. 场景举例: 比如某件商品价格变化,我们需要知…

手机里视频太大怎么压缩?压缩教程分享

现在视频文件的体积越来越大了&#xff0c;动不动就是几个GB起步&#xff0c;如果后期再剪辑处理一下&#xff0c;更是会占据更多的设备空间了&#xff0c;还会导致我们传输受到限制&#xff0c;这时候就需要我们对视频进行压缩处理&#xff0c;下面给大家分享几个简单的方法&a…

0基础学习VR全景平台篇 第83篇:智慧眼-怎么理解分类?

一、功能说明 分类可以理解为&#xff0c;为了方便城市运营工作的管理所实行的行政区划&#xff0c;如XXX乡镇、XXX街道等等。 二、后台编辑界面 1、点击【新增】&#xff0c;填写分类的名称&#xff0c;若有上一级分类&#xff0c;那么还需选择父级分类&#xff0c;建议从最…

Vue组件库

Vue组件库 ViteVue3TypescriptTSX 1、项目搭建 1.1、创建项目&#xff08;yarn&#xff09; D:\WebstromProject>yarn create vite yarn create v1.22.19 [1/4] Resolving packages... [2/4] Fetching packages... [3/4] Linking dependencies... [4/4] Building fresh pa…

Anaconda Prompt使用pip安装PyQt5-tools后无法打开Spyder或闪退

艹&#xff01;MLGBZD! 真TMD折腾人&#xff01; 出现原因&#xff1a; 首次安装完Anaconda3-2023.07-1-Windows-x86_64.exe后首次打开Spyder&#xff0c;此时是没有问题的&#xff0c;然后打开Anaconda Prompt&#xff0c;查看有哪些包&#xff0c;pip list 这时候开始首次安…

RCNA——单臂路由

一&#xff0c;实验背景 之前的VLAN实现的很多都是相同部门互相访问&#xff0c;不同部门无法访问。不过这次整来了一个路由器&#xff0c;领导说大部分的部门虽说有保密信息需要互相隔离&#xff0c;但是这些部门和其它部门也应该互相连通以方便工作交流。因此要配置新的环境&…

springboot汽车租赁后台java出租客户管理jsp源代码mysql

本项目为前几天收费帮学妹做的一个项目&#xff0c;Java EE JSP项目&#xff0c;在工作环境中基本使用不到&#xff0c;但是很多学校把这个当作编程入门的项目来做&#xff0c;故分享出本项目供初学者参考。 一、项目描述 springboot汽车租赁后台 系统有1权限&#xff1a;管理…

MySQL缓存策略

文章目录 一、MySQL缓存方案的作用二、提高MySQL访问性能的方式2.1 读写分离2.1.1 是什么&#xff1f;2.1.2 解决了什么&#xff1f;2.1.3 原理是什么&#xff1f; 2.2 连接池2.1.1 是什么&#xff1f;2.1.2 解决了什么&#xff1f;2.1.3 原理是什么&#xff1f; 2.3 异步连接2…

同步_异步请求和Ajax并利用axios框架简化

目录 同步和异步 原生的Ajax 创建XMLHttpRequest对象 常用方法 常用属性 axios框架 同步和异步 同步请求&#xff1a;发送请求后&#xff0c;会做出回应&#xff0c;回应的内容会覆盖浏览器中的内容&#xff0c;这样会打断其他正常的操作&#xff0c;显得不太友好&#…

springboot第35集:微服务与flutter安卓App开发

Google Playplay.google.com/apps/publis…[1]应用宝open.qq.com/[2]百度手机助手app.baidu.com/[3]360 手机助手dev.360.cn/[4]vivo 应用商店dev.vivo.com.cn/[5]OPPO 软件商店&#xff08;一加&#xff09;open.oppomobile.com/[6]小米应用商店dev.mi.com/[7]华为应用市场dev…

python项目virtualenv环境部署正式项目和后台运行实践

pycharm创建virtualenv环境的项目&#xff1a; 在本地虚拟环境项目路径下生成依赖包记录文件&#xff0c;然后上传到linux 服务器项目路径下&#xff1a; 注意注意&#xff1a;要在虚拟环境中生成&#xff0c;才能将所有的项目依赖包构建在 requirements.txt文件中。 pip3 fre…

学习笔记-JVM监控平台搭建

SpringBoot Actuator 1 引入依赖 <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-actuator</artifactId> </dependency>2 开启配置 # 暴露所有的监控点【含Prometheus】 management.endpoin…

Java课题笔记~ JSP内置对象

(1)九个内置对象 jsp的内置对象&#xff1a;JSP内置对象是不需要声明和创建就可以在JSP页面脚本中使用的成员变量。 九个内置对象&#xff1a; 1.out对象 在JSP页面中&#xff0c;经常需要向客户端发送文本内容&#xff0c;这时&#xff0c;可以使用out对象来实现。out对象…

Kubernetes Service 工作原理

本文介绍了 Kubernetes Service 的概念、原理和具体使用。 作者&#xff1a;沈亚军 爱可生研发团队成员&#xff0c;负责公司 DMP 产品的后端开发&#xff0c;爱好太广&#xff0c;三天三夜都说不完&#xff0c;低调低调… 本文来源&#xff1a;原创投稿 爱可生开源社区出品&am…