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

C++-vector模拟实现

###vector底层相当于是数组,查看源码可以发现,这个类的私有成员变量是三个迭代器;在实现时迭代器就可以当作是vector里面的元素的指针类型;

###vector是一个类模板,实现时也应当按照这样的写法用一个模板去实现,类模板vector中数据类型是T;

	private:iterator _start = nullptr;iterator _finish = nullptr;iterator _end_of_storage = nullptr;

_start指向vector的开始地址,_finish指向vector的有效元素的结束地址,_end_of_storage指向vector最大存储数据的地址; 

一、迭代器

typedef T* iterator;
typedef const T* const_iterator;//迭代器
iterator begin()
{return _start;
}
const_iterator begin()const
{return _start;
}
iterator end()
{return _finish;
}
const_iterator end()const
{return _finish;
}

二、容量

		size_t size()const{return _finish - _start;}size_t capacity()const{return _end_of_storage - _start;}bool empty()const{return size() == 0;}void reserve(size_t n){if (n > capacity()){size_t old_size = size();T* tmp = new T[n];//memcpy(tmp, _start, size() * sizeof(T));//浅拷贝for (size_t i = 0; i < old_size; i++)//深拷贝{tmp[i] = _start[i];}delete[] _start;_start = tmp;_finish = tmp + old_size;_end_of_storage = tmp + n;}}void resize(size_t n, const T& val = T())//匿名对象作为缺省值{if (n < capacity()){_finish = _start + n;}else{reserve(n);for (iterator i = _finish ; i < _start + n; i++){Insert(i,val);}}}

1、reserve

基本思路:开辟一个临时的数组存放T类型的数据,让这个数组的大小为指定的n,将vector对象中的数据全部放到这个临时数组中,再清空vector中的数据,最后让vector对象存放数据的地址就是这个临时数组的地址;

两个关键点:

  1. old_size:若是不先记录下size的话,后面给_finish重新赋值时使用到size(),size()函数中_size不再是原来的_size,而是tmp,此时用_size去减_finish会出错;
  2. 深、浅拷贝问题:若是使用memcpy就是浅拷贝,对于vector中是内置类型的数据可以,但是若是vector对象中涉及到自定义类型,例如string为数据的情况,浅拷贝拷贝的是地址,那么tmp中的数据的地址就是vector对象中的数据地址,之后再delete掉_start,就相当于把要存放数据的tmp中的数据给释放了,这样会让数据丢失;所以一个一个赋值,对于内置类型无影响,对于自定义类型,例如string的类型,就是赋值重载,string实现时是深拷贝,这样就解决了。

2、resize

基本思路:若是比原来的size小,那么就让_finish等于_start+n,这样就访问不到n之后的数据了;若是比原来的size大,没有给值就用匿名对象T(),对于内置类型给初始值(0、空指针、0.0一类),对于自定义类型,调用默认构造函数,实现部分:先扩容,再插入数据。

三、元素获取

		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 push_back(const T& val){if (_end_of_storage == _finish){reserve(size() == 0 ? 4 : 2 * capacity());}*_finish = val;_finish++;}void pop_back(){assert(size() > 0);_finish--;}iterator Insert(iterator pos, const T& val){assert(pos >= _start);assert(pos <= _finish);if (_end_of_storage == _finish){size_t len = pos - _start;reserve(size() == 0 ? 4 : 2 * capacity());pos = _start + len;}iterator end = _finish - 1;while (end >= pos){*(end + 1) = *(end);end--;}*pos = val;_finish++;return pos;}iterator Erase(iterator pos){assert(pos >= _start && pos < _finish);iterator tmp = pos + 1;while (tmp < _finish){*(tmp - 1) = *tmp;tmp++;}--_finish;return pos;}

1、insert和erase调用时迭代器失效的问题

insert:实现时若是涉及到空间不够要扩容,扩容时要先记录下pos和_start之间的距离,扩容之后_start不是之前的了,那么pos也要跟着更新;此时在调用insert时若是插入之后还要访问pos就要更新pos(因为pos已经改变了,若是不更新就使用原来的pos会找不到原来的数据)

就算没有扩容,空间够,insert之后使用到pos还是要先更新,因为此时的pos指向的是新插入进来的数据,而不是我们想要访问的原来的数据。

erase:erase的迭代器失效举一个例子:

	vector<int> v;v.push_back(1);v.push_back(2);v.push_back(3);v.push_back(4);v.push_back(5);container_print(v);cout << "size:" << v.size() << endl;cout << "capacity:" << v.capacity() << endl;vector<int>::iterator p = v.begin();while(p < v.end()){if (*p % 2 == 0){v.Erase(p);}p++;}container_print(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);container_print(v);cout << "size:" << v.size() << endl;cout << "capacity:" << v.capacity() << endl;vector<int>::iterator p = v.begin();while(p < v.end()){if (*p % 2 == 0){v.Erase(p);}p++;}container_print(v);

此时会发现有偶数没有删除完全,这是因为迭代器失效了

原因:erase的实现中删除了pos指向的数据之后返回pos,此时的pos指向的是原来要删除的数据的下一个数据,在这个删除偶数的例子中,{1,2,3,4,5}pos指向2.删除之后pos指向3,再加加就是指向4,刚好到了下一个偶数,删除之后,pos指向5,再加加,循环结束,这里只是一个巧合;数据是{1,2,3,4,4,5}删除完2,pos指向第一个4,删除之后,pos指向第二个4,再加加那么pos指向是5了,就跳过了这个4,此时就是迭代器失效了;

为了解决,erase要使用到pos就要更新迭代器;

	while(p < v.end()){if (*p % 2 == 0){p=v.Erase(p);}else{p++;}}

这样写,每次删除完之后,pos指向就是原来要删除数据的下一个数据,若是这个数是偶数那就继续删,是奇数就加加跳过,这样就能够不错过数据了。

五、构造

1、强制的默认构造写法

	vector() = default;//强制的默认构造

2、拷贝构造

		//拷贝构造vector(const vector<T>& v){reserve(v.capacity());for (auto it : v){push_back(it);}}

 3、赋值重载

		void clear(){_finish = _start;}void swap(const vector<T>& tmp){std::swap(_start, tmp._start);std::swap(_finish, tmp._finish);std::swap(_end_of_storage, tmp._end_of_storage);}//赋值重载(传统写法)vector<T>& operator=(const vector<T>& v){if (_start)//说明被赋值的对象不是空,要先清除{clear();}reserve(v.capacity());for (auto it : v){push_back(it);}}//赋值重载(现代写法)vector<T>& operator=(vector<T> tmp){swap(tmp);return *this;}

a、传统写法:

若是原vector不为空,先置为空,之后再扩容,扩到和v一样的大小,再使用拷贝构造那一套,将v的数据一个一个尾插进入要被赋值的vector

b、现代写法:

传参时用tmp接收赋值等式右边的vector,进行拷贝构造,之后再和赋值等式左边的vector进行交换;(现代写法一般要在拷贝构造写好的情况下进行);

4、迭代器初始化

		//迭代器初始化template <class InputIterator>//可以接收不同的迭代器,但是迭代器里面的数据的类型要相同vector(InputIterator first, InputIterator last){while (first != last){push_back(*first);first++;}}

这里使用到模板是为了接收各种类型的迭代器,但是值得注意的是这些迭代器的各自的容器里面的数据类型应该和此时的待构造的vector里面的数据保持一致,再不济也可以强制类型转换;

5、n个val去初始化

		//n个 val 去初始化vector(size_t n, const T& val = T())//不给值就用匿名对象,这个对象是 vector 里面的值{reserve(n);for (size_t i = 0; i < n; i++){push_back(val);}}

6、析构

		~vector(){if (_start){delete[] _start;_start = _finish = _end_of_storage = nullptr;}}

六、容器打印函数模板中的注意事项:

	template <class T>void vector_print(const vector<T>& v){//要写 typename,否则没有实例化的 vector<T> 不知道const_iterator是类型还是静态成员变量typename vector<T>:: const_iterator it = v.begin();//auto it = v.begin();while (it != v.end()){cout << *it << " ";it++;}cout << endl;}

这个函数模板定义在我们自己实现的vector类外面,用来打印vector,使用到迭代器,但是在写时注意这一行:

typename vector<T>:: const_iterator it = v.begin();

 要加上typename,否则编译器分不清这是类里面的静态成员变量还是类型,当然若是写成静态成员变量去使用,那就一定是静态成员变量,但是这里不能直接写,要在前面加上tyepname;第二种方法可以直接用auto自动生成类型去使用。

相关文章:

C++-vector模拟实现

###vector底层相当于是数组&#xff0c;查看源码可以发现&#xff0c;这个类的私有成员变量是三个迭代器&#xff1b;在实现时迭代器就可以当作是vector里面的元素的指针类型&#xff1b; ###vector是一个类模板&#xff0c;实现时也应当按照这样的写法用一个模板去实现&#…...

Activity

69[toc] 1.启停活动页面 1.Activity启动和结束 从当前页面跳到新页面 startActivity(new Intent(this, ActFinishActivity.class));从当前页面返回上一个页面&#xff0c;相当于关闭当前页面 finish();2.Activity生命周期 官方描述生命周期 onCreate&#xff1a;创建活…...

【力扣 | SQL题 | 每日四题】力扣1581, 1811, 1821, 1831

今天的题目就1811这个比较难&#xff0c;其他非常的基础。 1. 力扣1581&#xff1a;进店却未进行过交易的顾客 1.1 题目&#xff1a; 表&#xff1a;Visits ---------------------- | Column Name | Type | ---------------------- | visit_id | int | | customer…...

洛谷【P1955 [NOI2015] 程序自动分析】

反思&#xff1a; 这道题一眼就是并查集 但是数据太大 mle和re都是有可能的我看了题解才知道是离散化数组加并查集离散化再两个月前我觉得好难啊 那道题跟本看不懂 现在觉得还行 离散化思路&#xff1a; 需要一个离散记录数组----ls[N]用来记录下出现的数 步骤&#xff1a; …...

Swift并发笔记

1.同步和异步 说到线程的执行方式&#xff0c;最基本的一组概念是同步和异步。所谓同步&#xff0c;就是在操作执行完成之前&#xff0c;运行操作的这个线程都会被占用&#xff0c;直到函数最终被抛出或返回。Swift5.5之前&#xff0c;func关键字声明的所有的函数都是同步的。…...

React 组件命名规范

在 React 项目中&#xff0c;如果希望保持组件命名的一致性&#xff0c;并防止在引入时出现不同名称的问题&#xff0c;可以遵循以下的组件规范&#xff1a; 1、默认导出组件&#xff1a; 所有特殊要求的组件&#xff08;如页面组件或根组件&#xff09;应该使用 export defau…...

eNSP网络配置指南:IP设置、DNS、Telnet、DHCP与路由表管理

1.eNSP基本操作和路由器IP配置命令 登录设备&#xff1a;通过Console口或通过eNSP的Telnet/SSH客户端登录到设备。进入特权模式&#xff1a;输入system-view进入系统视图。接口配置&#xff1a; 进入接口视图&#xff0c;例如interface GigabitEthernet0/0/0。配置IP地址和子网…...

初步认识产品经理

产品经理 思考问题的维度 1️⃣为什么要抓住核心用户&#xff1f; 所有和产品有关系的群体就是用户&#xff0c;存在共性和差异了解用户的付费点&#xff0c;更好的优化产品是否使用&#xff1a;&#xff08;目标用户-已使用产品&#xff1a;种子用户-尝鲜&#xff1b;核心用…...

web前端面试中拍摄的真实js面试题(真图)

web前端面试中拍摄的真实js面试题&#xff08;真图&#xff09; WechatIMG258.jpeg WechatIMG406.jpeg WechatIMG407.jpeg WechatIMG922.jpeg WechatIMG1063.jpeg © 著作权归作者所有,转载或内容合作请联系作者 喜欢的朋友记得点赞、收藏、关注哦&#xff01;&#xff01;…...

python 人工智能 机器学习 当损失函数的数值变成 `nan` 时,这通常意味着在模型训练过程中出现了数值不稳定性以及解决办法,数据分析

当损失函数的数值变成 nan 时&#xff0c;这通常意味着在模型训练过程中出现了数值不稳定性。以下是一些可能导致这个问题的原因以及相应的解决方法&#xff1a; 1. **学习率过高**&#xff1a;如果学习率设置得过高&#xff0c;可能会导致梯度爆炸&#xff0c;从而导致损失函…...

Kafka快速实战与基本原理详解

笔记:https://note.youdao.com/ynoteshare/index.html?id=b0357bdb4821ed2e35ecdbdacd65aa06&type=note&_time=1727570043631 启动kafka之前先启动zookper 看看ZK里面都有什么数据 : 刚开始什么数据都没有 接下来启动kafka,启动好后,日志在这里看: 启动好了kaf…...

tftp传文件被服务器拒绝进入tftp: server error: (768) Access to staonline.pcap denied

环境&#xff1a;测试一个ac下挂ap&#xff0c;ap下的抓包文件传出时&#xff0c;出现问题&#xff1a; ac的wan口ip是192.168.186.167/24&#xff0c;gw是192.168.186.1&#xff0c;下挂ap的ip是192.168.202.199/24&#xff0c;ac上开子接口192.168.202.1/24&#xff0c;ac上开…...

express,生成用户登录后的 token

在 Node.js 中使用 Express 框架生成用户登录后的 token&#xff0c;通常会涉及到以下几个步骤&#xff1a; 设置 Express 应用&#xff1a;首先&#xff0c;你需要有一个基本的 Express 应用。安装必要的中间件&#xff1a;例如 jsonwebtoken&#xff08;JWT&#xff09;用于…...

银河麒麟桌面操作系统修改默认Shell为Bash

银河麒麟桌面操作系统修改默认Shell为Bash &#x1f490;The Begin&#x1f490;点点关注&#xff0c;收藏不迷路&#x1f490; 在银河麒麟桌面操作系统&#xff08;ARM版&#xff09;中&#xff0c;若要将默认Shell从Dash改为Bash&#xff0c;可执行以下步骤&#xff1a; 打开…...

卷积神经网络(Convolutional Neural Networks, CNN)

卷积神经网络&#xff08;Convolutional Neural Networks, CNN&#xff09;是深度学习领域中用于处理具有网格结构的输入&#xff08;如图像和视频&#xff09;的神经网络模型。下面以最简单、直观的方式概述CNN的主要流程及其基本概念&#xff1a; 1. 输入层 概念&#xff1a…...

SpringBoot系列 启动流程

文章目录 SpringApplicationSpringApplication#run 启动流程BootstrapContextSpringApplicationRunListenersprepareEnvironmentconfigureEnvironmentconfigurePropertySourcesconfigureProfiles 上下文初始化prepareContextrefreshContextprepareRefreshobtainFreshBeanFactor…...

vgg19提取特征

一般来说&#xff0c;大家使用VGG16&#xff0c;用的是第四列的网络架构&#xff0c;而使用VGG19&#xff0c;使用的就是第六列的网络架构。 使用vgg进行提取特征&#xff0c;在这个项目中&#xff0c;使用的就是每一块卷积层的第一层。 import torch.nn as nn from torchvis…...

Qt 中的 QChartView

深入理解 Qt 的 QChartView&#xff1a;图表展示与交互 QChartView 是 Qt Charts 模块中的一个核心类&#xff0c;它用于在 Qt 应用程序中显示图表&#xff0c;并支持多种用户交互方式。它继承自 QGraphicsView&#xff0c;通过封装 QChart&#xff0c;为用户提供了强大的图表…...

cheese安卓版纯本地离线文字识别插件

目的 cheese自动化平台是一款可以模拟鼠标和键盘操作的自动化工具。它可以帮助用户自动完成一些重复的、繁琐的任务&#xff0c;节省大量人工操作的时间。可以采用Vscode、IDEA编写&#xff0c;支持Java、Python、nodejs、GO、Rust、Lua。cheese也包含图色功能&#xff0c;识别…...

【C++】多肽

目录 一 多肽定义 1. 多肽的构成条件 1 例一 2 例二 2. 虚函数 3. 虚函数重写的两个意外 1 协变 2 析构函数的重写 二 关键字override 和 final 1. final 2.override 三 三重对比 1. 练习 四 多肽的原理 1. 多肽调用和普通调用 2.虚函数表 3. 分析 4. 原理 …...

idea大量爆红问题解决

问题描述 在学习和工作中&#xff0c;idea是程序员不可缺少的一个工具&#xff0c;但是突然在有些时候就会出现大量爆红的问题&#xff0c;发现无法跳转&#xff0c;无论是关机重启或者是替换root都无法解决 就是如上所展示的问题&#xff0c;但是程序依然可以启动。 问题解决…...

【Python】 -- 趣味代码 - 小恐龙游戏

文章目录 文章目录 00 小恐龙游戏程序设计框架代码结构和功能游戏流程总结01 小恐龙游戏程序设计02 百度网盘地址00 小恐龙游戏程序设计框架 这段代码是一个基于 Pygame 的简易跑酷游戏的完整实现,玩家控制一个角色(龙)躲避障碍物(仙人掌和乌鸦)。以下是代码的详细介绍:…...

超短脉冲激光自聚焦效应

前言与目录 强激光引起自聚焦效应机理 超短脉冲激光在脆性材料内部加工时引起的自聚焦效应&#xff0c;这是一种非线性光学现象&#xff0c;主要涉及光学克尔效应和材料的非线性光学特性。 自聚焦效应可以产生局部的强光场&#xff0c;对材料产生非线性响应&#xff0c;可能…...

微软PowerBI考试 PL300-选择 Power BI 模型框架【附练习数据】

微软PowerBI考试 PL300-选择 Power BI 模型框架 20 多年来&#xff0c;Microsoft 持续对企业商业智能 (BI) 进行大量投资。 Azure Analysis Services (AAS) 和 SQL Server Analysis Services (SSAS) 基于无数企业使用的成熟的 BI 数据建模技术。 同样的技术也是 Power BI 数据…...

【位运算】消失的两个数字(hard)

消失的两个数字&#xff08;hard&#xff09; 题⽬描述&#xff1a;解法&#xff08;位运算&#xff09;&#xff1a;Java 算法代码&#xff1a;更简便代码 题⽬链接&#xff1a;⾯试题 17.19. 消失的两个数字 题⽬描述&#xff1a; 给定⼀个数组&#xff0c;包含从 1 到 N 所有…...

CMake基础:构建流程详解

目录 1.CMake构建过程的基本流程 2.CMake构建的具体步骤 2.1.创建构建目录 2.2.使用 CMake 生成构建文件 2.3.编译和构建 2.4.清理构建文件 2.5.重新配置和构建 3.跨平台构建示例 4.工具链与交叉编译 5.CMake构建后的项目结构解析 5.1.CMake构建后的目录结构 5.2.构…...

【JVM】- 内存结构

引言 JVM&#xff1a;Java Virtual Machine 定义&#xff1a;Java虚拟机&#xff0c;Java二进制字节码的运行环境好处&#xff1a; 一次编写&#xff0c;到处运行自动内存管理&#xff0c;垃圾回收的功能数组下标越界检查&#xff08;会抛异常&#xff0c;不会覆盖到其他代码…...

2024年赣州旅游投资集团社会招聘笔试真

2024年赣州旅游投资集团社会招聘笔试真 题 ( 满 分 1 0 0 分 时 间 1 2 0 分 钟 ) 一、单选题(每题只有一个正确答案,答错、不答或多答均不得分) 1.纪要的特点不包括()。 A.概括重点 B.指导传达 C. 客观纪实 D.有言必录 【答案】: D 2.1864年,()预言了电磁波的存在,并指出…...

Nuxt.js 中的路由配置详解

Nuxt.js 通过其内置的路由系统简化了应用的路由配置&#xff0c;使得开发者可以轻松地管理页面导航和 URL 结构。路由配置主要涉及页面组件的组织、动态路由的设置以及路由元信息的配置。 自动路由生成 Nuxt.js 会根据 pages 目录下的文件结构自动生成路由配置。每个文件都会对…...

三体问题详解

从物理学角度&#xff0c;三体问题之所以不稳定&#xff0c;是因为三个天体在万有引力作用下相互作用&#xff0c;形成一个非线性耦合系统。我们可以从牛顿经典力学出发&#xff0c;列出具体的运动方程&#xff0c;并说明为何这个系统本质上是混沌的&#xff0c;无法得到一般解…...