C++中的vector使用与实现

一、vector的使用

1.1 vector的定义

是一种类模板

template < class T, class Alloc = allocator<T> > 
class vector;

其中的模板参数Alloc是在使用空间配置器(内存池),并给了缺省值,暂时不深究

1.2遍历方式

1.2.1下标+[]遍历

与sting类似,得益于其物理连续结构

1.2.2迭代器遍历

在书写迭代器类型的时候要注意加上模板类型

vector<int>::iterator it1=v1.begin();
1.2.2补:string也可以放到模板里

对应类型vector<string>

此时若要尾插一个string可以用上匿名对象的方法

如要尾插一个:

//①
string s1("张三");
v1.push_back(s1);//②
v1.push_back(string("李四"));//③
v1.push_back("王五");

这里的②就是用了匿名对象,而③用了单参数构造函数的隐式类型转换加匿名对象。

1.2.3范围for遍历

普通类型的用法与string类似,但是string的话最好

for(const string& e:v)
{//...}

这样可以避免string频繁地进行深拷贝,消耗时间

1.3数据的控制

1.3.1插入删除的操作

使用push_back和pop_back来完成,头插头删需要配合insert和erase来使用

void push_back (const value_type& val);void pop_back();iterator insert (iterator position, const value_type& val);iterator erase (iterator position);

1.3.2resize的操作

比原来短就缩小,比原来长就默认补齐,这里填补的数字有缺省值,可以选择补默认的(如0)或者自行指定

void resize (size_type n, value_type val = value_type());

1.4顺序表的嵌套

即顺序表的顺序表,如

vector<vector<int>>

此时顺序表中存的就是一个个地址,访问的时候可以采用类似二维数组的访问方式

vv[i][j];//实质上是
vv.operator[](i).operator[](j);

1.5顺序表常常配合算法sort使用,但排序的方式可能是自定义类型,怎么解决?

sort添加了一个重载,这个重载中有第三个参数

Compare comp

这是一种仿函数的应用,我们只要知道默认排升序,如果要改降序可以采用这一操作:

greater<int> gt;
sort(s1.begin(),s1.end(),gt);

greator这一模板属于std库,于它对应的还有一个less,传过去可以排升序。

二、vector的实现

2.1实现时的分文件问题

对于模板的声明与定义分离我们需要准备多行代码,因此建议都在一个文件.h中实现

2.1补:阅读源码的过程

①先看成员变量

②再看核心成员函数,如构造函数,vector中的尾插等等

③大致了解以后再展开看

2.2reserve实现时的陷阱

我们实现vector的时候有三个成员变量

_start      _finish      _end_of_storage

分别存储   初始位置,最后一个元素下一个位置,当前空间结束的下一个位置   三处的迭代器

size依靠指针相减得出大小,因此申请新空间以后size()会失效,需要提前记录size的大小方便使用。

2.3vector中的find问题

vector不再提供find,而是在算法algorithm头文件下有find函数来让stl统一使用。

2.4insert与erase涉及到的迭代器失效问题

2.4.1insert中

迭代器失效是扩容导致的,在插入时扩容后,_start和_finish会转移到新空间,那么原来标识插入位置的参数pos就会失效

直接在参数列表加引用可不可以?

答:不可以,例如当我们调用函数的语句写为

v1.insert(v1.begin(),0);

这样的时候,因为begin()是传值返回,而传值返回意味着会进行拷贝生成临时对象,临时对象具有常性,不可被修改

为此,我们以iterator作为返回值,出现类似

it=v1.insert(it,0);

这样的形式,更新一下it对应的迭代器。

2.4.2erase中

因为有时会erase最后一个数据,造成野指针,所以我们统一认为pos位置迭代器已失效,在VS中会在类型层面对它标识,不允许访问;在Linux中没有进行强制处理

此时我们的解决方案依旧是iterator作为返回值,更新一下来去除标识

2.4.2补:Linux中隐藏的陷阱

虽然在Linux中没有进行强制处理,但是也不能抱有侥幸心理,因为可能出现野指针问题和其他编译器无法运行的问题。

2.5实现拷贝构造时的问题

在自行实现vector的时候我们习惯直接用默认构造函数,但是因为拷贝构造也属于一种构造,所以在显示写完拷贝构造以后,编译器不会默认生成构造函数,这时可以用

2.5补:default关键字

vector()=default;

可以强制生成默认构造函数。

2.6迭代器区间进行构造中的问题

类模板的成员函数也可以写成函数模板,这里我们就用到了这一说法

template <class InputIterator>vector (InputIterator first, InputIterator last,const allocator_type& alloc = allocator_type());

我们之所以要写成函数模板,就是为了可以支持所有类型的迭代器进行区间初始化,它的实现逐步尾插即可

2.7缺省值T()与C++对内置类型的更新

对于泛型编程来说,有时我们需要给一种缺省值:来达到模板类型是什么,就给什么类型的值这一效果,为此出现了T(),

如vector的一种特殊构造:用n个val初始化vector就用到了

explicit vector (size_type n, const T& val = T(),const allocator_type& alloc = allocator_type());

其中,T()本质是自动调用构造函数,自定义类型完美解决,可是这样一来内置类型也需要构造了

2.7补:内置类型的构造

其实就是简单的赋值,如以下几种写法:

int a1=0;
int a2(1);
int a3=int();
int a4=int(1);

分别赋值0,1,0,1

2.8 vector(size_t n,const T& value)与vector(int n,const T& value)同时存在的原因

因为函数调用的冲突问题,

template <class InputIterator>vector (InputIterator first, InputIterator last);

vector (size_type n, const T& val = T())

同时存在的时候,我们的代码(int类型的特殊问题)

vector<int> v1(10,1);

本意是用10个1来初始化,却被上面的模板读取导致目的无法达到,因此重载一个函数来避免这种“抢调用”的情况。

2.9C++中{}的问题(initializer_list列表构造vector)

{}的功能不少,有

//多参数的隐式类型转换
A aa1={1,2};//单参数也可以用{}来隐式类型转换
B aa2={1};

要区分

std::vector<int> v1={1,2,3,4,5,6};

这里的隐式类型转换因为参数个数不固定,与上面不同,隐式类型转换到的新类型是属于C++11新支持的initializer_list类型

它有两种表示方法:

auto li={1,2,3,4,5,6};initializer_list<int> li={1,2,3,4,5,6};

此处li的类型为initializer_list

要想支持上述v1的顺利构造,需要写一个传参为initializer_list<T>类型的构造函数

库中:

vector (initializer_list<value_type> il,const allocator_type& alloc = allocator_type());

之后如

vector<int> v1({1,2,3});vector<int> v2={10,20,30};

都可以支持了。

2.10关于reverse对于含有资源管理的自定义类型的拷贝

对于内置类型和无资源管理的自定义类型,我们在实现reverse的时候进行memcpy对应的浅拷贝即可,但是对于如string类型的含有资源管理的自定义类型,我们在需要申请空间的时候会进行浅拷贝,delete销毁会导致原空间内容一并销毁,导致析构多次。

为此我们可以用下标+[]循环赋值来深拷贝解决。

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

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

相关文章

FFmpeg 4.3 音视频-多路H265监控录放C++开发四 :RGB颜色

一 RGB 的意义&#xff1f; 为什么要从RGB 开始讲起呢&#xff1f; 因为最终传输到显卡显示器的颜色都是RGB 即使能处理YUV的API&#xff0c;本质上也是帮你做了从 YUV 到 RGB的转换。 RGB888 表示 R 占8bit&#xff0c;G 占8bit&#xff0c;B 占8bit&#xff0c;也就是每一…

Gin框架操作指南10:服务器与高级功能

官方文档地址&#xff08;中文&#xff09;&#xff1a;https://gin-gonic.com/zh-cn/docs/ 注&#xff1a;本教程采用工作区机制&#xff0c;所以一个项目下载了Gin框架&#xff0c;其余项目就无需重复下载&#xff0c;想了解的读者可阅读第一节&#xff1a;Gin操作指南&#…

手撕布隆过滤器:原理解析与面试心得

前言 说来话长&#xff0c;话来说长。前些天我投了一些日常实习的简历&#xff0c;结果足足等了两个礼拜才收到面试通知&#xff0c;看来如今的行情确实是挺紧张的。当时我是满怀信心去的&#xff0c;心想这次一定要好好拷打面试官一番&#xff0c;结果没想到&#xff0c;自我…

腰背肌筋膜炎有哪些治疗方法

腰背肌筋膜炎主要表现为腰背部的疼痛、酸胀、僵硬、活动受限等症状。在疾病初期&#xff0c;症状可能相对较轻&#xff0c;通过休息、保暖、适当的物理治疗等&#xff0c;往往可以缓解症状&#xff0c;此时病情不算严重。如果患者不重视&#xff0c;继续保持不良的生活习惯&…

微服务架构 --- 使用RabbitMQ进行异步处理

目录 一.什么是RabbitMQ&#xff1f; 二.异步调用处理逻辑&#xff1a; 三.RabbitMQ的基本使用&#xff1a; 1.安装&#xff1a; 2.架构图&#xff1a; 3.RabbitMQ控制台的使用&#xff1a; &#xff08;1&#xff09;Exchanges 交换机&#xff1a; &#xff08;2&#…

什么是不同类型的微服务测试?

大家好&#xff0c;我是锋哥。今天分享关于【什么是不同类型的微服务测试&#xff1f;】面试题&#xff1f;希望对大家有帮助&#xff1b; 什么是不同类型的微服务测试&#xff1f; 1000道 互联网大厂Java工程师 精选面试题-Java资源分享网 微服务架构中的测试可以分为多种类…

WPF基础权限系统

一.开发环境 VisualStudio 2022NET SDK 8.0Prism 版本 8.1.97Sqlite 二. 功能介绍 WPF 基础权限系统&#xff0c;是一个支持前后端分离设计的 客户端(C/S)项目&#xff0c;该示例项目前端xaml使用UI库 &#xff0c;Material Design Themes UI 来构建用户界面&#xff0c;确保…

【into outfile写文件】

简介 select * from user into outfile C:/Users/ichunqiu/Desktop/PhpStudy2018/PHPTutorial/WWW/1.txt;用法的意思就是把user表中查询到的所有字段都导出到1.txt文件中 我们之前还有学到dumpfile&#xff0c;单是它只能导出一条数据 写入shell 测试注入点 usernameadmin&…

【工具】使用perf抓取火焰图

背景 当程序存在cpu性能问题时&#xff0c;我们需要找到是哪个函数占用较多的CPU&#xff0c;也就是找出热点函数&#xff1b;perf的火焰图就是这个用途 安装 在Linux系统中&#xff0c;perf 是 Linux 内核提供的性能分析工具&#xff0c;它通常包含在内核源代码包中。大多数…

编码方式知识整理【ASCII、Unicode和UTF-8】

编码方式 一、ASCII编码二、Unicode 编码三、UTF-8编码四、GB2312编码五、GBK编码 计算机中对数据的存储为二进制形式&#xff0c;但采用什么样的编码方式存储&#xff0c;效率更高。主要编码方式有 ASCII、Unicode、UTF-8等。 英文一般为1个字节&#xff0c;汉字一般为3个字节…

Linux 线程互斥

1.相关背景概念 临界资源&#xff1a;多线程执行流共享的资源就叫做临界资源 临界区&#xff1a;每个线程内部&#xff0c;访问临界资源的代码&#xff0c;就叫做临界区 #include <iostream> #include <pthread.h> #include <string> #include <vector…

mac安装jdk8

这里写自定义目录标题 一、下载JDK8二、安装JDK8三、配置环境变量3.1 找到JDK安装目录3.2 打开终端&#xff1a;3.3 输入如下配置&#xff1a;3.3 查看配置是否成功&#xff1a; 一、下载JDK8 oracle官网下载或从下面链接获取 https://download.csdn.net/download/qq_44107684…

【小沐学Golang】基于Go语言搭建静态文件服务器

文章目录 1、简介2、安装2.1 安装版2.2 压缩版 3、基本操作3.1 go run3.2 go build3.3 go install3.4 go env3.5 go module 4、文件服务器4.1 filebrowser4.2 gohttpserver4.3 goFile 5、FAQ5.1 go.mod 为空5.2 超时 结语 1、简介 https://golang.google.cn/ Go语言诞生于2007…

day02 -- docker

1.docker的介绍 Docker 是一个开源的应用容器引擎&#xff0c;基于 Go语言 并遵从 Apache2.0 协议开源。Docker 可以让开发者打包他们的应用以及依赖包到一个轻量级、可移植的容器中&#xff0c;然后发布到任何流行的 Linux 机器上&#xff0c;也可以实现虚拟化。容器是完全使…

●day 35 动态规划part01

第九章 动态规划part01 动态规划的类别 理论基础 动态规划下五步曲&#xff1a; 1、确定dp数组&#xff08;dp table&#xff09;以及下标的含义 2、确定递推公式 3、dp数组如何初始化 4、确定遍历顺序 5、打印dp数组 代码随想录 斐波那契数 代码随想录 动态规划5部曲 cla…

高级语言源程序转换为可执行目标文件

将高级语言源程序转换为可执行目标文件的过程通常包括以下几个主要步骤&#xff1a; ​ 1. 预处理&#xff08;Preprocessing&#xff09;&#xff1a; 由谁完成预处理器&#xff08;cpp&#xff09;操作处理源代码中的预处理指令&#xff08;如宏定义、文件包含、条件编译等&…

Linux——动态卷的管理

确保已经设置了对应的动态卷的驱动&#xff08;provisioner 制备器&#xff09;基于动态驱动创建对应的存储类创建PVC &#xff08;PVC 将会自动根据大小、访问模式等创建PV&#xff09;Pod的spec 中通过volumes 和 volumemounts 来完成pvc 的绑定和pvc对应pv的挂载删除pod 不…

Linux网络编程(七)-TCP协议客户端及代码实现

1.TCP的客户端代码流程简述 这一章将为大家讲解Socket通信中客户端的实现过程&#xff0c;还是先上图&#xff0c;请大家了解客户端的步骤 可以看到&#xff0c;相比服务端&#xff0c;客户端的步骤简单的很多。事实上这种情况比较多&#xff0c;比如一个服务端会有多个客户端…

JMeter模拟并发请求

PostMan不是严格意义上的并发请求工具&#xff0c;实际是串行的&#xff0c;如果需要测试后台接口并发时程序的准确性&#xff0c;建议采用JMeter工具。 案例&#xff1a;JMeter设置20个并发卖票请求&#xff0c;查看后台是否存在超卖的情况 方式一&#xff1a;一共10张票&…

TrickMo 安卓银行木马新变种利用虚假锁屏窃取密码

近期&#xff0c;研究人员在野外发现了 TrickMo Android 银行木马的 40 个新变种&#xff0c;它们与 16 个下载器和 22 个不同的命令和控制&#xff08;C2&#xff09;基础设施相关联&#xff0c;具有旨在窃取 Android 密码的新功能。 Zimperium 和 Cleafy 均报道了此消息。 …