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

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范围内,肯定会崩溃的。

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

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

 

 

 

 

相关文章:

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

cplusplus.com/reference/vector/vector/https://cplusplus.com/reference/vector/vector/ vector在实际中非常的重要&#xff0c;在实际中我们熟悉常见的接口就可以&#xff0c;有了string的基础&#xff0c;vector其实大体使用方法上二者是类似的&#xff1a; 这里我们先给…...

[工程构建] 使用 pkg-config 的 cmake 模板

可执行文件 # 1) cmake basic cmake_minimum_required(VERSION 3.12) #cmake version check set(CXX_STANDARD 17) #c standard version)# 2) project info #auto generated variables as below: #PROJECT_NAME: "hello" #hello_BINARY_DIR: build root dir #hello_…...

MATLAB 注释快捷键

matlab 前言单行注释多行注释 快捷键使用菜单 前言 单行注释 % 这是一个单行注释 x 10; % 这是另一个单行注释多行注释 %{ 这是一个多行注释 它可以包含多行文本 x 10; % 这行代码也会被注释掉 %}快捷键 在 MATLAB 编辑器中&#xff0c;可以使用快捷键来快速注释和取消注…...

8.优化存储过程的性能(8/10)

优化存储过程的性能 1.引言 存储过程是数据库系统中预先编写好的SQL语句集合&#xff0c;它们被保存在数据库服务器上&#xff0c;可以在需要时被调用执行。存储过程的使用可以提高数据库操作的效率&#xff0c;减少网络通信&#xff0c;并且可以封装复杂的逻辑&#xff0c;使…...

Django发送邮件代理服务器配置

根路由下配置 MAIL_BACKEND django.core.mail.backends.smtp.EmailBackend EMAIL_HOST smtp.qq.com EMAIL_HOST_USER 66897079qq.com EMAIL_HOST_PASSWORD aavlzhzvqorbcahcEMAIL_PORT 465 EMAIL_USE_SSL True发送邮件 message "<p>尊敬的用户您好&#xff…...

uniapp__微信小程序使用秋云ucharts折线图双轴

1、子组件 <template><view class"charts-box"><qiun-data-charts type"line":opts"computedOpts":chartData"chartData"/></view> </template><script> export default {props: {chartData: {t…...

云原生运维 - 旅程(简约版)

1. 入门阶段 理论学习&#xff1a; 了解云计算和容器技术的基本概念。学习Docker基础知识&#xff0c;包括容器创建、镜像管理等。阅读Kubernetes官方文档的入门部分&#xff0c;了解Kubernetes的核心概念。 实操练习&#xff1a; 安装Docker环境。运行你的第一个Docker容器…...

2014年国赛高教杯数学建模B题创意平板折叠桌解题全过程文档及程序

2014年国赛高教杯数学建模 B题 创意平板折叠桌 某公司生产一种可折叠的桌子&#xff0c;桌面呈圆形&#xff0c;桌腿随着铰链的活动可以平摊成一张平板&#xff08;如图1-2所示&#xff09;。桌腿由若干根木条组成&#xff0c;分成两组&#xff0c;每组各用一根钢筋将木条连接…...

PyCharm打开及配置现有工程(详细图解)

本文详细介绍了如何利用Pycharm打开一个现有的工程&#xff0c;其中包括编译器的配置。 PyCharm打开及配置现有工程 1、打开工程2、配置编译器 1、打开工程 双击PyCharm软件&#xff0c;点击左上角 文件 >> 打开(O)… 选中想要打开的项目之后点击“确定” 2、配置编译器…...

CSP-J

CSP那些事儿 OI赛制是啥OI赛制下的CCF-CSPCSP简介CSP-J考试&#xff08;仅山东&#xff09;考试时间考试地点考试结构 写在最后有趣的代码&#xff1a; OI赛制是啥 OI赛制&#xff0c;不详细说了&#xff0c;就是一股脑做好几个题&#xff0c;一起提交的比赛&#xff08;通俗易…...

Linux系统:Linux中ln命令用法

ln命令功能 将一个文件或目录在同一个文件系统或者另一个不同的文件系统的某个位置建立一个链接&#xff0c;类似windows系统中的超链接&#xff0c;这样当我们在链接处访问被链接的目录或文件时就可以通过此链接来访问&#xff0c;不必要再进入要访问的文件系统中。 建立链接…...

在SpringBoot+VUE中 实现登录-RSA的加密解密

步骤-先理清楚在动手 前端首先调用后端的公钥接口,在前端加密密码传输至后端登录接口后端用私钥解密码拿着用户名去数据库查询出来的盐值加密的 密码1用私钥解密密码登录密码加盐值得到 密码2比较密码1与密码2,相同则登录成功&#xff0c;跳转首页&#xff5c;其他页面 前端实…...

基于Android11简单分析audio_policy_configuration.xml

开篇先贴上一个高通的例子&#xff0c;后续基于此文件做具体分析。 1 <?xml version"1.0" encoding"UTF-8" standalone"yes"?> 2 <!-- Copyright (c) 2016-2019, The Linux Foundation. All rights reserved 3 Not a Contribut…...

kafka-manager修改zookeeper端口号后启动仍然连接2181端口

问题描述&#xff1a; zookeeper默认端口号修改为了2182&#xff0c;kafka-manager的配置文件application.conf中也已经修改了zkhosts为新的端口号&#xff0c;然而启动kafka-manger时报错连接连接超时&#xff0c;发现连接的还是2181端口&#xff0c;很奇怪&#xff1f;&…...

RabbitMQ 入门(三)SpringAMQP

一、Spring AMQP 简介 SpringAMQP是基于RabbitMQ封装的一套模板&#xff0c;并且还利用SpringBoot对其实现了自动装配&#xff0c;使用起来非常方便。 SpringAmqp的官方地址&#xff1a;https://spring.io/projects/spring-amqp SpringAMQP提供了三个功能&#xff1a; - 自动…...

celery 项目中mysql 数据库连接数耗尽事故记录

python 项目中使用 celery 中导致mysql数据库连接耗尽记录【mysql数据库连接池使用错误】 结论&#xff1a;由于使用 celery 进行项目的多任务管理&#xff0c;在worker任务定义的过程中&#xff0c;使用了 dbutils 中的 PooledDB 连接池进行 mysql数据库连接&#xff0c; 因此…...

Python数据分析-Scipy科学计算法

1.认识Scipy SciPy&#xff08;发音为 "Sigh Pie"&#xff09;是一个开源的 Python 算法库和数学工具包。 通常与 NumPy、Matplotlib 和 pandas 等库一起使用&#xff0c;这些库共同构成了 Python 的科学计算基础。 2.使用Scipy基本函数 2.1 引用Scipy函数 impor…...

【Python Django + Vue】酒店在线预订系统:用技术说话!

&#x1f393; 作者&#xff1a;计算机毕设小月哥 | 软件开发专家 &#x1f5a5;️ 简介&#xff1a;8年计算机软件程序开发经验。精通Java、Python、微信小程序、安卓、大数据、PHP、.NET|C#、Golang等技术栈。 &#x1f6e0;️ 专业服务 &#x1f6e0;️ 需求定制化开发源码提…...

禁用微软的windos安全中心

目录 一、为什么禁用 二、WDControl_1.5.0程序禁用windows安全中心 步骤1--- 步骤2--- 三、禁用widows安全中心成功 一、为什么禁用 描述&#xff1a;下载第三方软件常常会收到病毒防护秒杀&#xff0c; 第1---直接无法下载 第2---提前下载在U盘解压会被干掉程序文件 …...

2.html编辑器介绍

html编辑器介绍 HTML 编辑器推荐 理论上我们可以使用记事本进行html编码和开发&#xff0c;但是在实际开发html页面的时候&#xff0c;使用一些专业的开发工具可以使我们更加快速和高效的进行开发&#xff0c;下面介绍几种开发工具&#xff1a; VS Code&#xff1a;https://…...

iOS 26 携众系统重磅更新,但“苹果智能”仍与国行无缘

美国西海岸的夏天&#xff0c;再次被苹果点燃。一年一度的全球开发者大会 WWDC25 如期而至&#xff0c;这不仅是开发者的盛宴&#xff0c;更是全球数亿苹果用户翘首以盼的科技春晚。今年&#xff0c;苹果依旧为我们带来了全家桶式的系统更新&#xff0c;包括 iOS 26、iPadOS 26…...

ESP32读取DHT11温湿度数据

芯片&#xff1a;ESP32 环境&#xff1a;Arduino 一、安装DHT11传感器库 红框的库&#xff0c;别安装错了 二、代码 注意&#xff0c;DATA口要连接在D15上 #include "DHT.h" // 包含DHT库#define DHTPIN 15 // 定义DHT11数据引脚连接到ESP32的GPIO15 #define D…...

企业如何增强终端安全?

在数字化转型加速的今天&#xff0c;企业的业务运行越来越依赖于终端设备。从员工的笔记本电脑、智能手机&#xff0c;到工厂里的物联网设备、智能传感器&#xff0c;这些终端构成了企业与外部世界连接的 “神经末梢”。然而&#xff0c;随着远程办公的常态化和设备接入的爆炸式…...

django blank 与 null的区别

1.blank blank控制表单验证时是否允许字段为空 2.null null控制数据库层面是否为空 但是&#xff0c;要注意以下几点&#xff1a; Django的表单验证与null无关&#xff1a;null参数控制的是数据库层面字段是否可以为NULL&#xff0c;而blank参数控制的是Django表单验证时字…...

安卓基础(Java 和 Gradle 版本)

1. 设置项目的 JDK 版本 方法1&#xff1a;通过 Project Structure File → Project Structure... (或按 CtrlAltShiftS) 左侧选择 SDK Location 在 Gradle Settings 部分&#xff0c;设置 Gradle JDK 方法2&#xff1a;通过 Settings File → Settings... (或 CtrlAltS)…...

Ubuntu系统复制(U盘-电脑硬盘)

所需环境 电脑自带硬盘&#xff1a;1块 (1T) U盘1&#xff1a;Ubuntu系统引导盘&#xff08;用于“U盘2”复制到“电脑自带硬盘”&#xff09; U盘2&#xff1a;Ubuntu系统盘&#xff08;1T&#xff0c;用于被复制&#xff09; &#xff01;&#xff01;&#xff01;建议“电脑…...

使用SSE解决获取状态不一致问题

使用SSE解决获取状态不一致问题 1. 问题描述2. SSE介绍2.1 SSE 的工作原理2.2 SSE 的事件格式规范2.3 SSE与其他技术对比2.4 SSE 的优缺点 3. 实战代码 1. 问题描述 目前做的一个功能是上传多个文件&#xff0c;这个上传文件是整体功能的一部分&#xff0c;文件在上传的过程中…...

数据结构:泰勒展开式:霍纳法则(Horner‘s Rule)

目录 &#x1f50d; 若用递归计算每一项&#xff0c;会发生什么&#xff1f; Horners Rule&#xff08;霍纳法则&#xff09; 第一步&#xff1a;我们从最原始的泰勒公式出发 第二步&#xff1a;从形式上重新观察展开式 &#x1f31f; 第三步&#xff1a;引出霍纳法则&…...

写一个shell脚本,把局域网内,把能ping通的IP和不能ping通的IP分类,并保存到两个文本文件里

写一个shell脚本&#xff0c;把局域网内&#xff0c;把能ping通的IP和不能ping通的IP分类&#xff0c;并保存到两个文本文件里 脚本1 #!/bin/bash #定义变量 ip10.1.1 #循环去ping主机的IP for ((i1;i<10;i)) doping -c1 $ip.$i &>/dev/null[ $? -eq 0 ] &&am…...

【Ftrace 专栏】Ftrace 参考博文

ftrace、perf、bcc、bpftrace、ply、simple_perf的使用Ftrace 基本用法Linux 利用 ftrace 分析内核调用如何利用ftrace精确跟踪特定进程调度信息使用 ftrace 进行追踪延迟Linux-培训笔记-ftracehttps://www.kernel.org/doc/html/v4.18/trace/events.htmlhttps://blog.csdn.net/…...