[C++]vector(超详细)

在学习完了string后,我们来学习新的STL容器vector,是真正的属于STL中的一员,vector也是STL的基础容器,英文释义是向量,其实实质上就是顺序表。

在这个部分我们会学习的非常快,第一个原因是由于vector的设计更加简单,第二个原因是因为string和vector都是数组类型的,我们学会了string,这个就信手拈来了。

模拟实现代码放到文章尾部 

目录

vector的使用

vector的标准成员函数

vector的构造函数

析构函数

赋值重载

vector的基本操作

vector的遍历

reserve

resize

insert

erase​编辑

swap​编辑

vector>

vector的模拟实现

析构函数

​编辑 

拷贝构造 

赋值重载

​编辑 

​编辑 迭代器构造

基本的函数 

reserve

push_back

 pop_back

insert

迭代器失效

 erase

迭代器部分

resize

vector的使用

vector的标准成员函数

vector的构造函数

构造函数比较简单,就有四种构造方法,默认构造(无参构造),带参构造,迭代器区间构造,拷贝构造。

如图所示,为默认构造,带参构造,和迭代器区间构造,以及拷贝构造。

析构函数

析构函数会帮我们自动释放空间,也并不复杂

赋值重载

赋值重载也只有一个函数,也并不复杂

我们由此可以对比string,vector是非常简单的一个容器,它的接口也简洁了很多,不会像之前string那样复杂

不过vector是一个标准的模板

 第一个模板参数就是vector所要存储的类型,第二个模板参数是一个空间配置器,内存池,在这个部分内,我们不用关系内存池是什么,也不需要显示传递,我们就大概理解,内存池就是和new,malloc一样的,可以获取内存,但是可以提高效率

使用vector,我们要加头文件<vector> 

vector的基本操作

相比于string,vector里没有length,只有size,这也使容器变得简洁。

由于很多接口与string的用法类似,我们这里介绍方法会简洁一些

vector的遍历

我们和之前一样三种方法进行遍历

第一种用operate[]直接访问元素

第二章用迭代器,第三种用范围for

当用范围for遍历的时候,我建议带上引用&,因为如果vector数据很长,那就会一直拷贝,效率很低,而带上&就可以解决这个问题 

reserve

我们用一段代码来看一下,vector的扩容规律是否与string类似

在VS中的扩容如下,可以看到扩容,大概还是1.5倍扩容,但是有些地方做了特殊处理,向上取整。 

 我们再在g++中编译这个代码,看看扩容情况

可以看到在g++编译后,是按标准2倍进行扩容的 

我们看到reserve中,大致与string的reserve相同,编译器会给vector对象开辟n的空间,但是实际上会开辟比n大的空间

但这里有一点,与string不同

在string里,由于string开辟的空间是不具有约束力的,因此当传入的参数比实际空间小,会取决于编译器进行缩容。(在vs中不缩容,在g++下进行缩容)

而在vector中,vector开辟的空间,在任何情况下是不会缩容的

如下代码所示,VS情况下不缩容

 如下所示,为g++编译环境,也不会发生缩容

 按照道理来说,内存一般也不应该发生缩容

开辟的一段空间,一般都是从首地址进行释放,将空间全部释放,不应该发生从中间释放空间的情形。

resize

resize在string内也是有这个接口的,但是使用的并不多,但是在vector中,resize会被经常调用

 resize会把contains数据个数扩到N,那么这里就有三种情况

 关于插入的数据,如果你传值了就按数据就按照传入的值插入,如果没有传入值,那就会调用对应的默认构造对数据进行初始化

vector直接提供了尾插和尾删,但是没有提供头插头删,如果你要使用头插头删就必须使用insert

insert

比起string的insert,这里的设计就简洁了很多,不至于搞一些花里胡哨的

但是这里不支持下标插入了,只是支持迭代器,不够如果想在特点位置插入元素,只需要用迭代器

it+pos即可,毕竟迭代器支持基本运算的

erase

erase也是不支持下标删除,但是支持迭代器删除 ,所以效果与insert也一样

swap

swap的作用就是,防止我们自己使用swap,降低效率,因此提供了专门的接口 

以上就是一些重要的接口,对于STL,我们可以在实践过程中,用到的时候再去查阅资料学习即可

vector不支持流插入和流提取,由于string只需要按照顺序打印,遇到\0停止即可,所以有特定的格式,但是vector具有太多的不确定性,因此不支持。但如果我们想自己实现,也很容易,因此我们可以根据我们的需要,自己实现。

这里有一个问题,因为都是顺序表,能否用vector<char>替代string呢?

其中一个重大区别就是'\0'的区别,vector没有'\0',而string有'\0',因此string可以很好的兼容C,而vector不行,你可能会想那我在后面加上'\0'不就可以了吗?

那么问题又来了,你是在char类型下加'\0',那在int,double情况下,你的'\0'是什么意思。

因此string要单独拿出来,它有很大的意义。

vector<vector<int>>

vector<vector<int>>实际上就和我们二维数组一样,我们从大概从底层的角度来描述一下,存储结构,如图所示

用operate[]来访问 

vector的模拟实现

我们先看一下库里vector的源码

 库里面很喜欢用typedef,把一些类型变成iterator

我们不去关心复杂的源码,我们从入门的来理解

 我们可以根据之前的经验,以及图中成员变量的名字,来猜测一下这三个变量代表什么 

我们可以通过底层源码的其他函数,来反向推导这三个变量的含义,有兴趣的可以自己去推导一下

三个变量代表的含义如图所示

 含义很简单,start就是数据的开头指针,finish是数据结束的位置,end_of_storage是存储位置的结尾。

我们这里仿照底层源码的方式来模拟实现vector

命名风格也和库里一致,这也是一种新的风格

析构函数

 

拷贝构造 

我们这里的拷贝构造写的很有意思,直接将元素pushback进this对象中,直接使用写过的接口完成拷贝构造的实现。

由于 我们vector在写拷贝构造之前,不需要写默认构造,编译器会自动提供一个默认构造,但是当手动写拷贝构造后,编译器就不会给我们提供默认构造了,我们可以用C++11中的新语法,强制生成一份默认构造

赋值重载

传统写法

 

现代写法

 迭代器构造

我们在类模板中,还可以再创建模板,不如迭代器构造

 这样不但我们能用vector迭代器区间进行构造,我们还可以用链表对vector进行构造

 

基本的函数 

size,capacity,我们直接用指针-指针的方式,就能求出来相应的值

而判空函数直接通过判断头指针是否等于尾指针即可。

operate[]重载,首先用断言检查是否越界,然后直接用下标返回对应的元素即可,不过要注意这里的返回值是引用类型,不然没法进行修改。 

reserve

reserve函数还是之前那一套逻辑,判断n是否大于capacity,正常进行开辟新空间,拷贝数据,删除旧空间,指向新空间,修改变量值。 

但是如果我们这样写就坑了,因为size = _finish-_start,由于我们已经修改了_start,size就不能再代表个数了,我们可以修改两个变量修改的先后顺序,但是先修改finish,再修改start,一点不符合逻辑,因此我们不如再添一个变量记录大小,这样也更清晰明了。

如果我们这样写的话 ,如果vector存的数据是指针类型(拷贝时需要深拷贝的类型)时,就会出现错误,原因是memcpy是浅拷贝,当浅拷贝完又delete[]原空间,就会导致新的tmp指向的是已释放的空间,因此我们需要换成深拷贝 

push_back

尾插操作也是比较简单的,先判断是否需要扩容,需要就扩容,然后直接将数据放到尾部,++finish即可

 pop_back

断言判断是否为空,然后直接--finish

insert

根据我们之前的经验,可以写出如下的代码

我们通过监视窗口,可以发现这个代码在扩容之前是正确的,但是在扩容之后,我们再运行插入逻辑,会发现程序挂掉了 

 我们发现,当end>=pos后,程序还在运行,说明while这里面有问题

这个问题就是经典的迭代器失效

迭代器失效

第一种情况,实质上就是野指针的情况

画个图,我们就很好理解了

那这个问题也很好解决,我们只需要记录 pos的相对位置,在扩容后,重新赋值即可

如图所示

第二种迭代器失效的情况

迭代器位置的意义已经变化了

 

由于数据的挪动,当前的迭代器已经改变了意义,也是一种迭代器失效的体现

在VS平台中,在实现operate*的部分,会进行强制的检查,如果发生了迭代器失效,会直接中止程序,规避风险。不过在g++的编译条件下并不会报错,但是这是非常危险的事情,因为当你迭代器失效后,你修改的是哪个数据,你也不清楚。因此,我们得出结论,在不同的平台下,关于迭代器失效的检查是不同的,我们在insert,erase 后就不要再访问迭代器,或者重新给迭代器赋值。

 erase

erase的实现,首先assert判断pos位置是否合法,然后依次挪动数据,最后修改finish的值即可

由于erase删除元素后会迭代器失效,因此库里面提供的函数返回值,为删除元素的下一位元素,我们只需要将迭代器重新接收返回值即可。

迭代器部分

由于我们成员变量的定义,对于迭代器部分,非常简单,如下所示

resize

我们resize也要根据n的值和capacity和size的关系分成三种情况来实现

当n<size,我们直接删除数据到n

我们只需要修改_finish的值即可

当n>size,就是要插入数据,但是我们要进行检查扩容 。我们不管需不需要扩容,直接reserve,传入n,然后依次插入数据。

实现如下所示

平常我们使用resize都是用来初始化的。 

以上就是vector的全部内容

下面是模拟实现的代码

namespace study
{template<class T>class vector{public:typedef T* iterator;typedef const T* const_iterator;/*vector(){}*/// C++11 前置生成默认构造vector() = default;vector(const vector<T>& v){reserve(v.size());for (auto& e : v){push_back(e);}}// 类模板的成员函数,还可以继续是函数模版template <class InputIterator>vector(InputIterator first, InputIterator last){while (first != last){push_back(*first);++first;}}vector(size_t n, const T& val = T()){reserve(n);for (size_t 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);}}void clear(){_finish = _start;}// v1 = v3/*vector<T>& operator=(const vector<T>& v){if (this != &v){clear();reserve(v.size());for (auto& e : v){push_back(e);}}return *this;}*/void swap(vector<T>& v){std::swap(_start, v._start);std::swap(_finish, v._finish);std::swap(_end_of_storage, v._end_of_storage);}// v1 = v3//vector& operator=(vector v)vector<T>& operator=(vector<T> v){swap(v);return *this;}~vector(){if (_start){delete[] _start;_start = _finish = _end_of_storage = nullptr;}}iterator begin(){return _start;}iterator end(){return _finish;}const_iterator begin() const{return _start;}const_iterator end() const{return _finish;}void reserve(size_t n){if (n > capacity()){size_t old_size = size();T* tmp = new T[n];//memcpy(tmp, _start, old_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, T val = T()){if (n < size()){_finish = _start + n;}else{reserve(n);while (_finish < _start + n){*_finish = val;++_finish;}}}size_t size() const{return _finish - _start;}size_t capacity() const{return _end_of_storage - _start;}bool empty() const{return _start == _finish;}void push_back(const T& x){// 扩容if (_finish == _end_of_storage){reserve(capacity() == 0 ? 4 : capacity() * 2);}*_finish = x;++_finish;}void pop_back(){assert(!empty());--_finish;}iterator insert(iterator pos, const T& x){assert(pos >= _start);assert(pos <= _finish);// 扩容if (_finish == _end_of_storage){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;return pos;}void erase(iterator pos){assert(pos >= _start);assert(pos < _finish);iterator it = pos + 1;while (it != end()){*(it - 1) = *it;++it;}--_finish;}T& operator[](size_t i){assert(i < size());return _start[i];}const T& operator[](size_t i) const{assert(i < size());return _start[i];}private:iterator _start = nullptr;iterator _finish = nullptr;iterator _end_of_storage = nullptr;};

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

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

相关文章

算法 class 005 (对数器C语言实现)

对数器的概念&#xff1a; 用来测试你的算法是否正确。 怎么做呢&#xff1f; 1&#xff1a;比如&#xff0c;写个冒泡排序&#xff0c;作为对比的对象 2&#xff1a;生成一个随机数 数组&#xff0c;用来测试 3&#xff1a;用冒泡排序和你想要验证的那个排序算法&#xff0c;同…

风力涡轮机缺陷检测数据集,91.4%准确识别率,18912张图片,支持yolo,PASICAL VOC XML,COCO JSON格式的标注

风力涡轮机缺陷检测数据集&#xff0c;91.4&#xff05;准确识别率&#xff0c;18912张图片&#xff0c;支持yolo&#xff0c;PASICAL VOC XML&#xff0c;COCO JSON格式的标注 数据集下载&#xff1a; &#xff59;&#xff4f;&#xff4c;&#xff4f; &#xff56;&#…

termux配置nginx+php

只能以默认用户u0_axx运行,修改用户会报错An error occurred.或者file no found 安装nginx pkg install nginx安装php-fpm pkg install nginx修改nginx配置文件, nano ../usr/etc/nginx/nginx.conf#端口必须设置在1024以上(1024以下需要root,但php-fpm不能以root用户运行,n…

机器人对物体重定向操作的发展简述

物体重定向操作的发展简述 前言1、手内重定向和外部重定向2、重定向原语3、重定向状态转换网络4、连续任意姿态的重定向5、利用其他环境约束重定向总结Reference 前言 对于一些特殊的任务&#xff08;如装配和打包&#xff09;&#xff0c;对物体放置的位姿由明确的要求&#…

k8s基础(1)—Kubernetes-Pod

一、Pod简介 Pod是Kubernetes&#xff08;k8s&#xff09;系统中可以创建和管理的最小单元&#xff0c;是资源对象模型中由用户创建或部署的最小资源对象模型‌。Pod是由一个或多个容器组成的&#xff0c;这些容器共享存储和网络资源&#xff0c;可以看作是一个逻辑的主机‌。…

【ArcGISPro/GeoScenePro】解决常见的空间参考和投影问题

修复空间参考缺失的图像 数据 https://arcgis.com/sharing/rest/content/items/535efce0e3a04c8790ed7cc7ea96d02d/data 查看属性坐标 查看属性范围 范围值并不是零或接近于零。 这意味着栅格具有范围,因此其已正确进行

集线器,交换机,路由器,mac地址和ip地址知识记录总结

一篇很不错的视频简介 基本功能 从使用方面来说&#xff0c;都是为了网络传输的标识&#xff0c;和机器确定访问对象 集线器、交换机和路由器 常听到路由器和集线器&#xff0c;下面是区别&#xff1a; 集线器 集线器&#xff1a;一个简单的物理扩展接口数量的物理硬件。…

跳跃表(跳表)是什么

为什么要有跳表 正常链表只能一个一个往下走但是如果我直到我的目标位置就在链表的中部但是我还得一步一步走过去很浪费时间&#xff0c;所以跳表就是在正常链表的基础上添加了多步跳跃的指针。 什么是跳表 跳表&#xff08;Skip List&#xff09;是一种概率型的数据结构&am…

【自动驾驶汽车通讯协议】RGMII通信技术详解

文章目录 0. 前言1. RGMII概述1.1 RGMII说明1.2 RGMII作用&目的 2. RGMII的发展历史2.1 MII (Media Independent Interface)2.2 GMII (Gigabit Media Independent Interface)2.3 RGMII (Reduced Gigabit Media Independent Interface) 3. 架构设计3.1 接口信号3.2 信号时序…

浏览器选中文字样式

效果 学习 Chrome: 支持 ::selection。Firefox: 支持 :-moz-selection 和 ::selection。Safari: 支持 ::selection。Internet Explorer: 支持 :-ms-selection。Microsoft Edge: 支持 ::-ms-selection 和 ::selection。 代码 <!DOCTYPE html> <html lang"en&qu…

RAG实战:本地部署ragflow+ollama(linux)

1.部署ragflow 1.1安装配置docker 因为ragflow需要诸如elasticsearch、mysql、redis等一系列三方依赖&#xff0c;所以用docker是最简便的方法。 docker安装可参考Linux安装Docker完整教程&#xff0c;安装后修改docker配置如下&#xff1a; vim /etc/docker/daemon.json {…

如何免费解锁 IPhone 网络

您是否担心 iPhone 上的网络锁定&#xff1f;如果您的 iPhone 被锁定到特定运营商&#xff0c;解锁它可以连接到不同的运营商。好吧&#xff0c;我们为您准备了一份指南。 iPhone运营商免费解锁将是小菜一碟。在我们的解锁运营商 iphone 免费指南中。我们为您提供了一份简介&am…

人工智能安全——联邦学习的安全攻击与防护

参考论文FedMDFG: Federated Learning with Multi-Gradient Descent and Fair Guidance (AAAI-2023) 背景 随着人工智能技术的飞速发展&#xff0c;隐私保护和数据安全越来越受到重视。联邦学习&#xff08;Federated Learning, FL&#xff09;作为一种分布式隐私保护的机器学…

R机器学习:神经网络算法的理解与实操,实例解析

神经网络算法是一种模仿生物神经网络&#xff08;尤其是人脑&#xff09;结构和功能的算法。它由大量相互连接的节点&#xff08;称为神经元&#xff09;组成&#xff0c;这些神经元组织成层&#xff0c;通过传递信号来处理信息。神经网络算法在机器学习、人工智能等领域中扮演…

wsl linux CUDA安装、卸载、清理、版本降级、升级过程详解

目录 1 前言2 卸载与重新安装2.1 列出所有与 CUDA 相关的包2.2 卸载 CUDA2.3 删除残留文件2.4 移除 CUDA 仓库包2.5 删除仓库目录2.6 移除所有 CUDA 相关包2.7 删除 CUDA 的优先级配置文件2.8 查看所有 APT 源中的 CUDA 条目2.9 移除或禁用不需要的 CUDA 仓库源2.10 删除所有 A…

牛客网刷题 ——C语言初阶——OR76 两个整数二进制位不同个数

1. 牛客网题目&#xff1a;OR76 两个整数二进制位不同个数 牛客网OJ链接 描述&#xff1a; 输入两个整数&#xff0c;求两个整数二进制格式有多少个位不同 输入描述&#xff1a;两个整数 输出描述&#xff1a;二进制不同位的个数 示例1 输入&#xff1a;22 33 输出&#xff1a…

【AWS SDK PHP】This operation requests `sigv4a` auth schemes 问题处理

使用AWS SDK碰到的错误&#xff0c;其实很简单&#xff0c;要装个扩展库 保持如下 Fatal error: Uncaught Aws\Auth\Exception\UnresolvedAuthSchemeException: This operation requests sigv4a auth schemes, but the client currently supports sigv4, none, bearer, sigv4-…

设计模式 结构型 装饰器模式(Decorator Pattern)与 常见技术框架应用 解析

装饰器模式&#xff08;Decorator Pattern&#xff09;&#xff0c;又称为包装器模式&#xff08;Wrapper Pattern&#xff09;&#xff0c;是一种结构型设计模式。它允许在不改变原有对象结构的基础上&#xff0c;动态地给对象添加一些新的职责&#xff08;即增加其额外功能&a…

<论文>聊聊初代LLaMA

一、摘要 本文介绍来自Meta的论文《LLaMA: Open and Efficient Foundation Language Models》&#xff0c;这篇2023年的研究发布了开源的LLaMA系列大模型&#xff0c;轰动一时。 译文&#xff1a; 我们推出了 LLaMA&#xff0c;一系列参数规模从 70 亿到 650 亿的基础语言模型。…

把vue项目或者vue组件发布成npm包或者打包成lib库文件本地使用

将vue项目发布成npm库文件&#xff0c;第三方通过npm依赖安装使用&#xff1b;使用最近公司接了一个项目&#xff0c;这个项目需要集成到第三方页面&#xff0c;在第三方页面点击项目名称&#xff0c;页面变成我们的项目页面&#xff1b;要求以npm库文件提供给他们&#xff1b;…