C++:STL:vector类常用函数介绍(附加部分重要函数模拟实现)

cplusplus.com/reference/vector/vector/icon-default.png?t=O83Ahttps://cplusplus.com/reference/vector/vector/

vector在实际中非常的重要,在实际中我们熟悉常见的接口就可以,有了string的基础,vector其实大体使用方法上二者是类似的:

这里我们先给出我们简单模拟实现时所用的成员函数:

private:iterator _start;iterator _finish;iterator _end_of_storage;

下文在介绍与string实现方式基本相同的地方,或者用法与string中相同函数基本一致的地方,均会直接以表格加实现代码的方式进行介绍,以便于我们重点介绍vector中我们需要了解的两个新知识:迭代器失效和更深层次的深浅拷贝问题。 

一,vector的定义

1.1(constructor)构造函数声明

cplusplus.com/reference/vector/vector/vector/icon-default.png?t=O83Ahttps://cplusplus.com/reference/vector/vector/vector/其中我们需要重点掌握的是以下几个构造函数:

以下是我们的简单实现代码:

template<class T>
class vector
{
public:	vector():_start(nullptr),_finish(nullptr),_end_of_storage(nullptr){}template<class InputIterator>vector(InputIterator first, InputIterator last):_start(nullptr),_finish(nullptr),_end_of_storage(nullptr){assert(first && last);InputIterator itor = first;for (; itor < last; itor++){push_back(*itor);}}vector(size_t n, const T& value = T()):_start(nullptr), _finish(nullptr), _end_of_storage(nullptr){resize(n, value);}vector(const vector<T>& v):_start(nullptr),_finish(nullptr),_end_of_storage(nullptr){reserve(v.capacity());_finish = _start + v.size();for (size_t i = 0; i < size(); i++){(*this)[i] = v[i];}}vector<T>& operator=(vector<T> v){swap(this, v);return *this;}
}

其中的swap,push_back,resize以及reserve与string中的功能基本一样,我们后面在来介绍实现它们, 这里我们先不管它们如何实现。我们要重点说的是这里拷贝构造时遇到的深浅拷贝问题,这里先埋个伏笔。

1.2vector iterator 的使用

cplusplus.com/reference/vector/vector/begin/

cplusplus.com/reference/vector/vector/end/

 cplusplus.com/reference/vector/vector/rbegin/

 cplusplus.com/reference/vector/vector/rend/

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

二,vector 空间增长问题 

cplusplus.com/reference/vector/vector/size/ 

cplusplus.com/reference/vector/vector/capacity/

cplusplus.com/reference/vector/vector/empty/

cplusplus.com/reference/vector/vector/resize/

cplusplus.com/reference/vector/vector/reserve/

 

size_t size()const
{return _finish - _start;
}size_t capacity()const
{return _end_of_storage - _start;
}
void resize(size_t n, const T& pos = T())
{while (size() < n){push_back(pos);}if (n <= size())_finish = _start + n;
}

接下来的reserve我们需要单独拎出来讲,先来看下我们在实现string的reserve时的模拟实现代码:

void string::reserve(size_t n)
{if (n > _capacity){char* tmp = new char[n + 1] {'\0'};strcpy(tmp, _str);delete[] _str;_str = tmp;_capacity = n;}
}

从string的模拟实现我们第一时间会想到去用memcpy来完成对n>capacity时的数据拷贝。当然,这种方法对于我们所有的内置类型都没有问题,比如vector<int>,vector<char>等等, 但如果是vector<string>这种成员元素是标准库类型的数据呢, 如果我们只是简单的去拷贝每一个字节,其实是对每一个元素的浅拷贝,就像这样:

void reserve(size_t n)
{if (n > capacity()){size_t oldsize = size();T* tmp = new T[n];if (_start){memcpy(tmp, _start, oldsize * sizeof(T));delete[] _start;}_start = tmp;_finish = _start + oldsize;_end_of_storage = _start + n;}
}

很明显的问题,接下来tmp中的string对象都会指向一块随即空间,也就是它们中的成员指针全部成了野指针。直接就会报错,解决方法其实也很简单,我们只需要改用for循环去拷贝数据即可,虽然会些许浪费时间,但是是我们简单模拟时一种较好的解决方法:

void reserve(size_t n)
{
if (n > capacity())
{size_t oldsize = size();T* tmp = new T[n];if (_start){for (size_t i = 0; i < oldsize; i++){tmp[i] = _start[i];}delete[] _start;}_start = tmp;_finish = _start + oldsize;_end_of_storage = _start + n;}
}

 除此之外,需要注意的点还有:

1.capacity的代码在vs和g++下分别运行会发现,vs下capacity是按1.5倍增长的,g++是按2倍增长的。这个问题经常会考察,不要固化的认为,vector增容都是2倍,具体增长多少是根据具体的需求定义的。vs是PJ版本STL,g++是SGI版本STL。
2.reserve只负责开辟空间,如果确定知道需要用多少空间,reserve可以缓解vector增容的代
价缺陷问题。
3.resize在开空间的同时还会进行初始化,影响size。

三,vector 增删查改 

 cplusplus.com/reference/vector/vector/reserve/

cplusplus.com/reference/vector/vector/push_back/

 cplusplus.com/reference/vector/vector/pop_back/

cplusplus.com/reference/algorithm/find/?kw=find

cplusplus.com/reference/vector/vector/insert/

cplusplus.com/reference/vector/vector/erase/

cplusplus.com/reference/vector/vector/swap/

 cplusplus.com/reference/vector/vector/operator[]/

//access
T& operator[](size_t i)
{assert(i < size());return _start[i];
}const T& operator[](size_t i)const
{assert(i < size());return _start[i];
}//modify
void push_back(T n)
{if (_finish == _end_of_storage){reserve(capacity() == 0 ? 4 : capacity() * 2);}*_finish = n;_finish++;	
}void pop_back()
{assert(size());_finish--;
}void swap(vector<T>& v)
{std::swap(_start, v._start);std::swap(_finish, v._finish);std::swap(_end_of_storage, v._end_of_sstorage);
}

 

TIPS:迭代器失效问题

注意:Linux下,g++编译器对迭代器失效的检测并不是非常严格,处理也没有vs下极端。

这里我们借助insert与erase来介绍:

iterator insert(iterator pos, const T& x){assert(pos >= _start && pos <= _finish);if (_finish == _end_of_storage){size_t len = pos - _start;reserve(capacity() == 0 ? 4 : capacity() * 2);pos = _start + len;}iterator itor = _finish;while (itor >= pos){*(itor + 1) = *itor;--itor;}*pos = x;_finish++;return pos;
}iterator erase(iterator pos)
{assert(pos >= _start && pos < _finish);iterator itor = pos;while (itor < _finish - 1){*pos = *(pos + 1);}_finish--;return pos;
}

这里给出insert与erase主要是为了便于我们去对照vs与linux下实现代码的差异,下面我们来看第一种失效:

1.扩容引起的野指针:

我们来看下面一组例子:

void test_vector3()
{ELY::vector<int> v1;//ELY是我实现的vector所在的命名空间v1.push_back(1);v1.push_back(2);v1.push_back(3);v1.push_back(4);//v1.push_back(4);for (auto e : v1){cout << e << " ";}cout << endl;int x;cin >> x;auto it = find(v1.begin(), v1.end(), x);if (it != v1.end()){v1.insert(it, 10 * x);cout << *it << endl;}for (auto e : v1){cout << e << " ";}cout << endl;
}

这串代码在vs下的运行结果如下图:

显然此时我们的it已经失效,因为扩容后,it如果没有更新,此时it指向的空间已经被释放了,导致it迭代器成了野指针,致使迭代器失效。

2.删除数据导致数据的挪动致使迭代器失效

void test_vector4(){std::vector<int> v1;v1.push_back(1);v1.push_back(2);v1.push_back(3);v1.push_back(4);for (auto e : v1){cout << e << " ";}cout << endl;int x;cin >> x;auto it = find(v1.begin(), v1.end(), x);if (it != v1.end()){v1.erase(it);cout << *it << endl;}}

 vs下面会直接报错:

g++ 下面正常运行:

再来看这样一组例子,删除数据中的所有偶数:

void test_vector4()
{std::vector<int> v1;v1.push_back(1);v1.push_back(2);v1.push_back(2);v1.push_back(3);v1.push_back(4);for (auto e : v1){cout << e << " ";}cout << endl;auto it = v1.begin();while (it != v1.end()){if (*it % 2 == 0)v1.erase(it);++it;}for (auto i : v1){cout << i << ' ';}cout << endl;
}

vs下面还是直接报错:

 

g++上面运行:

这里直接端错误。

换一组数据我们会发现偶数并没有被删干净。

///

从上述三个例子中可以看到:SGI STL中,迭代器失效后,代码并不一定会崩溃,但是运行
结果肯定不对,如果it不在begin和end范围内,肯定会崩溃的。

所以我们在使用迭代器的时候,一定要注意迭代器失效的问题,要及时更新我们的迭代器。

迭代器失效解决办法:在使用前,对迭代器重新赋值即可。

 

 

 

 

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

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

相关文章

ScriptableObject基本使用

使用方法 自定义类继承ScriptableObject 可以在类内部增加数据或者数据类&#xff0c;一般用于配置 注意事项 给继承ScriptableObject的类增加CreateAssetMenu特性。 CreateAssetMenu一般默认三个参数 第一个参数是父目录 第二个参数是父目录的子选项 第三个参数是可以…

k8s yaml编写规范

yaml简介 yaml 是专门用来写配置文件的语言 yaml文件也是一种配置文件类型&#xff0c;后缀名是.yaml或.yml都可以 yaml语法规则 大小写敏感使用缩进表示层级关系&#xff08;不能用Tab&#xff0c;只能用空格&#xff09;相同层级的元素左对齐#号表示单行注释字符串可以不用…

多态(二)

1.多态的原理 虚函数表 class Base { public:virtual void Func1(){cout << "Func1()" << endl;} private:int _b 1; };b对象是8bytes&#xff0c;除了_b成员&#xff0c;还多一个__vfptr放在对象的前面(注意有些 平台可能会放到对象的最后面&#xf…

微信小程序启动不起来,报错凡是以~/包名/*.js路径的文件,都找不到,试过网上一切方法,最终居然这么解决的,【避坑】命运的齿轮开始转动

app.json "resolveAlias": {"~/*": "/*"},文件代码也没有问题&#xff0c;网上的方法试过来了&#xff0c;大模型AI也问过遍&#xff0c;熬夜到凌晨2点半&#xff0c;最不可思议的是居然是因为微信开发者工具版本的问题&#xff0c;我真的是笑死…

量化之一:均值回归策略

文章目录 均值回归策略理论基础数学公式 关键指标简单移动平均线&#xff08;SMA&#xff09;标准差Z-Score 交易信号实际应用优缺点分析优点缺点 结论 实践backtrader参数&#xff1a;正常情况&#xff1a;异常情况&#xff1a; 均值回归策略 均值回归&#xff08;Mean Rever…

JAVA-数据结构-排序

1.直接插入排序 1.原理&#xff1a;和玩扑克牌一样&#xff0c;从左边第二个牌开始&#xff0c;选中这个&#xff0c;和前面的所有牌比较&#xff0c;插在合适的位置 public static void insertsort(int[] arr){//直接插入排序for (int i 1; i < arr.length; i) {//此循环…

1 机器学习之引言

傍晚小街路面上沁出微雨后的湿润&#xff0c;和煦的细风吹来&#xff0c;抬头看看天边的晚霞&#xff0c;嗯&#xff0c;明天又是一个好天气。走到水果摊旁&#xff0c;挑了个根蒂蜷缩、敲起来声音浊响的青绿西瓜&#xff0c;一边满心期待着皮薄肉厚瓤甜的爽落感&#xff0c;一…

STM32 GPIO

GPIO&#xff08;通用输入输出口&#xff0c;General Purpose Input Output&#xff09;接口的功能是让嵌入式处理器能够通过软件灵活地读出或控制单个物理引脚上的高、低电平&#xff0c;实现内核和外部系统之间的信息交换。 GPIO是嵌入式处理器使用最多的外设&#xff0c;能够…

React和Vue区别,以及注意事项

目录 一、语法和框架特性的差异 二、开发习惯和注意事项 三、特别注意事项 一、语法和框架特性的差异 模板语法&#xff1a; Vue使用了类似于传统HTML的模板语法&#xff0c;通过双大括号{{ }}进行插值&#xff0c;而React则使用了JSX语法。在Vue中&#xff0c;你可以直接在…

甲虫身体图像分割系统源码&数据集分享

甲虫身体图像分割系统源码&#xff06;数据集分享 [yolov8-seg-EfficientRepBiPAN&#xff06;yolov8-seg-C2f-FocusedLinearAttention等50全套改进创新点发刊_一键训练教程_Web前端展示] 1.研究背景与意义 项目参考ILSVRC ImageNet Large Scale Visual Recognition Challen…

SQL Server 计算两个时间相差

在 SQL Server 中&#xff0c;计算两个时间字符串之间的差值 首先将这些字符串转换成日期/时间类型&#xff08;如 datetime 或 datetime2&#xff09;然后使用日期函数来计算它们之间的差异。 1、计算两个时间字符串之间的差值 案例&#xff1a;计算 starttime 和 endtime …

android——activity之间数据共享(单例等)

一、使用 Intent 传递数据&#xff08;适用于简单数据传递&#xff0c;且在 Activity 启动时&#xff09; 二、使用静态变量&#xff08;简单但有风险&#xff09; 原理 在一个类中定义静态变量&#xff0c;例如一个 Application 类或者一个专门用于存储共享数据的工具类。两个…

毕设开源 大数据电影数据分析与可视化系统(源码+论文)

文章目录 0 前言1 项目运行效果2 设计概要3 最后 0 前言 &#x1f525;这两年开始毕业设计和毕业答辩的要求和难度不断提升&#xff0c;传统的毕设题目缺少创新和亮点&#xff0c;往往达不到毕业答辩的要求&#xff0c;这两年不断有学弟学妹告诉学长自己做的项目系统达不到老师…

简单粗暴理解GNN、GCN、GAT

GNN 思想&#xff1a;近朱者赤近墨者黑 GNN的流程&#xff1a; 聚合&#xff08;把邻居的信息贴到自己身上来&#xff0c;作为它自己特征的补足&#xff09;更新循环&#xff08;为什么要多次&#xff1f;看以下例子&#xff09; GNN能干嘛&#xff1f; 1.结点分类&#xf…

【多线程】多线程(12):多线程环境下使用哈希表

【多线程环境下使用哈希表&#xff08;重点掌握&#xff09;】 可以使用类&#xff1a;“ConcurrentHashMap” ★ConcurrentHashMap对比HashMap和Hashtable的优化点 1.优化了锁的粒度【最核心】 //Hashtable的加锁&#xff0c;就是直接给put&#xff0c;get等方法加上synch…

STM32的时钟复位控制单元(RCU/RCC)技术介绍

在嵌入式系统开发中&#xff0c;时钟管理和复位控制是确保微控制器稳定运行的关键因素。时钟复位控制单元&#xff08;Reset and Clock Control, RCU/RCC&#xff09; 是 STM32 系列微控制器中的一个重要外设&#xff0c;负责管理系统的时钟源、分频器、外设时钟以及复位功能。…

【网络协议】TCP协议常用机制——延迟应答、捎带应答、面向字节流、异常处理,保姆级详解,建议收藏

&#x1f490;个人主页&#xff1a;初晴~ &#x1f4da;相关专栏&#xff1a;计算机网络那些事 前几篇文章&#xff0c;博主带大家梳理了一下TCP协议的几个核心机制&#xff0c;比如保证可靠性的 确认应答、超时重传 机制&#xff0c;和提高传输效率的 滑动窗口及其相关优化机…

构建可以ssh连接的容器镜像

构建可以ssh连接的容器镜像 构建可以通过ssh进行连接容器镜像&#xff0c;实现远程登录容器的目的。 ubuntu ssh容器镜像 你可以使用以下Dockerfile来构建一个可以SSH的容器镜像&#xff1a; FROM ubuntu:20.04MAINTAINER lldhsds# 配置apt国内源 COPY sources.list /etc/a…

云原生开发 - 工具镜像(简约版)

在微服务和云原生环境中&#xff0c;容器化的目标之一是尽可能保持镜像小型化以提高启动速度和减少安全风险。然而&#xff0c;在实际操作中&#xff0c;有时候需要临时引入一些工具来进行调试、监控或问题排查。Kubernetes提供了临时容器&#xff08;ephemeral containers&…

MyBatis-Plus 的核心插件及其使用介绍

MyBatis-Plus 是基于 MyBatis 的增强工具&#xff0c;为简化 MyBatis 的开发提供了诸多功能扩展。它的目标是减少重复代码、提高开发效率&#xff0c;提供了 CRUD&#xff08;Create, Read, Update, Delete&#xff09;操作的简化方法以及多种实用插件。以下是 MyBatis-Plus 的…