C++:STL:vector类常用函数介绍(附加部分重要函数模拟实现)
cplusplus.com/reference/vector/vector/
https://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/
https://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在实际中非常的重要,在实际中我们熟悉常见的接口就可以,有了string的基础,vector其实大体使用方法上二者是类似的: 这里我们先给…...
[工程构建] 使用 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 编辑器中,可以使用快捷键来快速注释和取消注…...
8.优化存储过程的性能(8/10)
优化存储过程的性能 1.引言 存储过程是数据库系统中预先编写好的SQL语句集合,它们被保存在数据库服务器上,可以在需要时被调用执行。存储过程的使用可以提高数据库操作的效率,减少网络通信,并且可以封装复杂的逻辑,使…...
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>尊敬的用户您好ÿ…...
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. 入门阶段 理论学习: 了解云计算和容器技术的基本概念。学习Docker基础知识,包括容器创建、镜像管理等。阅读Kubernetes官方文档的入门部分,了解Kubernetes的核心概念。 实操练习: 安装Docker环境。运行你的第一个Docker容器…...
2014年国赛高教杯数学建模B题创意平板折叠桌解题全过程文档及程序
2014年国赛高教杯数学建模 B题 创意平板折叠桌 某公司生产一种可折叠的桌子,桌面呈圆形,桌腿随着铰链的活动可以平摊成一张平板(如图1-2所示)。桌腿由若干根木条组成,分成两组,每组各用一根钢筋将木条连接…...
PyCharm打开及配置现有工程(详细图解)
本文详细介绍了如何利用Pycharm打开一个现有的工程,其中包括编译器的配置。 PyCharm打开及配置现有工程 1、打开工程2、配置编译器 1、打开工程 双击PyCharm软件,点击左上角 文件 >> 打开(O)… 选中想要打开的项目之后点击“确定” 2、配置编译器…...
CSP-J
CSP那些事儿 OI赛制是啥OI赛制下的CCF-CSPCSP简介CSP-J考试(仅山东)考试时间考试地点考试结构 写在最后有趣的代码: OI赛制是啥 OI赛制,不详细说了,就是一股脑做好几个题,一起提交的比赛(通俗易…...
Linux系统:Linux中ln命令用法
ln命令功能 将一个文件或目录在同一个文件系统或者另一个不同的文件系统的某个位置建立一个链接,类似windows系统中的超链接,这样当我们在链接处访问被链接的目录或文件时就可以通过此链接来访问,不必要再进入要访问的文件系统中。 建立链接…...
在SpringBoot+VUE中 实现登录-RSA的加密解密
步骤-先理清楚在动手 前端首先调用后端的公钥接口,在前端加密密码传输至后端登录接口后端用私钥解密码拿着用户名去数据库查询出来的盐值加密的 密码1用私钥解密密码登录密码加盐值得到 密码2比较密码1与密码2,相同则登录成功,跳转首页|其他页面 前端实…...
基于Android11简单分析audio_policy_configuration.xml
开篇先贴上一个高通的例子,后续基于此文件做具体分析。 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端口
问题描述: zookeeper默认端口号修改为了2182,kafka-manager的配置文件application.conf中也已经修改了zkhosts为新的端口号,然而启动kafka-manger时报错连接连接超时,发现连接的还是2181端口,很奇怪?&…...
RabbitMQ 入门(三)SpringAMQP
一、Spring AMQP 简介 SpringAMQP是基于RabbitMQ封装的一套模板,并且还利用SpringBoot对其实现了自动装配,使用起来非常方便。 SpringAmqp的官方地址:https://spring.io/projects/spring-amqp SpringAMQP提供了三个功能: - 自动…...
celery 项目中mysql 数据库连接数耗尽事故记录
python 项目中使用 celery 中导致mysql数据库连接耗尽记录【mysql数据库连接池使用错误】 结论:由于使用 celery 进行项目的多任务管理,在worker任务定义的过程中,使用了 dbutils 中的 PooledDB 连接池进行 mysql数据库连接, 因此…...
Python数据分析-Scipy科学计算法
1.认识Scipy SciPy(发音为 "Sigh Pie")是一个开源的 Python 算法库和数学工具包。 通常与 NumPy、Matplotlib 和 pandas 等库一起使用,这些库共同构成了 Python 的科学计算基础。 2.使用Scipy基本函数 2.1 引用Scipy函数 impor…...
【Python Django + Vue】酒店在线预订系统:用技术说话!
🎓 作者:计算机毕设小月哥 | 软件开发专家 🖥️ 简介:8年计算机软件程序开发经验。精通Java、Python、微信小程序、安卓、大数据、PHP、.NET|C#、Golang等技术栈。 🛠️ 专业服务 🛠️ 需求定制化开发源码提…...
禁用微软的windos安全中心
目录 一、为什么禁用 二、WDControl_1.5.0程序禁用windows安全中心 步骤1--- 步骤2--- 三、禁用widows安全中心成功 一、为什么禁用 描述:下载第三方软件常常会收到病毒防护秒杀, 第1---直接无法下载 第2---提前下载在U盘解压会被干掉程序文件 …...
2.html编辑器介绍
html编辑器介绍 HTML 编辑器推荐 理论上我们可以使用记事本进行html编码和开发,但是在实际开发html页面的时候,使用一些专业的开发工具可以使我们更加快速和高效的进行开发,下面介绍几种开发工具: VS Code:https://…...
iOS 26 携众系统重磅更新,但“苹果智能”仍与国行无缘
美国西海岸的夏天,再次被苹果点燃。一年一度的全球开发者大会 WWDC25 如期而至,这不仅是开发者的盛宴,更是全球数亿苹果用户翘首以盼的科技春晚。今年,苹果依旧为我们带来了全家桶式的系统更新,包括 iOS 26、iPadOS 26…...
Vue2 第一节_Vue2上手_插值表达式{{}}_访问数据和修改数据_Vue开发者工具
文章目录 1.Vue2上手-如何创建一个Vue实例,进行初始化渲染2. 插值表达式{{}}3. 访问数据和修改数据4. vue响应式5. Vue开发者工具--方便调试 1.Vue2上手-如何创建一个Vue实例,进行初始化渲染 准备容器引包创建Vue实例 new Vue()指定配置项 ->渲染数据 准备一个容器,例如: …...
uniapp微信小程序视频实时流+pc端预览方案
方案类型技术实现是否免费优点缺点适用场景延迟范围开发复杂度WebSocket图片帧定时拍照Base64传输✅ 完全免费无需服务器 纯前端实现高延迟高流量 帧率极低个人demo测试 超低频监控500ms-2s⭐⭐RTMP推流TRTC/即构SDK推流❌ 付费方案 (部分有免费额度&#x…...
3403. 从盒子中找出字典序最大的字符串 I
3403. 从盒子中找出字典序最大的字符串 I 题目链接:3403. 从盒子中找出字典序最大的字符串 I 代码如下: class Solution { public:string answerString(string word, int numFriends) {if (numFriends 1) {return word;}string res;for (int i 0;i &…...
HashMap中的put方法执行流程(流程图)
1 put操作整体流程 HashMap 的 put 操作是其最核心的功能之一。在 JDK 1.8 及以后版本中,其主要逻辑封装在 putVal 这个内部方法中。整个过程大致如下: 初始判断与哈希计算: 首先,putVal 方法会检查当前的 table(也就…...
【笔记】WSL 中 Rust 安装与测试完整记录
#工作记录 WSL 中 Rust 安装与测试完整记录 1. 运行环境 系统:Ubuntu 24.04 LTS (WSL2)架构:x86_64 (GNU/Linux)Rust 版本:rustc 1.87.0 (2025-05-09)Cargo 版本:cargo 1.87.0 (2025-05-06) 2. 安装 Rust 2.1 使用 Rust 官方安…...
uniapp手机号一键登录保姆级教程(包含前端和后端)
目录 前置条件创建uniapp项目并关联uniClound云空间开启一键登录模块并开通一键登录服务编写云函数并上传部署获取手机号流程(第一种) 前端直接调用云函数获取手机号(第三种)后台调用云函数获取手机号 错误码常见问题 前置条件 手机安装有sim卡手机开启…...
Chrome 浏览器前端与客户端双向通信实战
Chrome 前端(即页面 JS / Web UI)与客户端(C 后端)的交互机制,是 Chromium 架构中非常核心的一环。下面我将按常见场景,从通道、流程、技术栈几个角度做一套完整的分析,特别适合你这种在分析和改…...
Linux中《基础IO》详细介绍
目录 理解"文件"狭义理解广义理解文件操作的归类认知系统角度文件类别 回顾C文件接口打开文件写文件读文件稍作修改,实现简单cat命令 输出信息到显示器,你有哪些方法stdin & stdout & stderr打开文件的方式 系统⽂件I/O⼀种传递标志位…...
高考志愿填报管理系统---开发介绍
高考志愿填报管理系统是一款专为教育机构、学校和教师设计的学生信息管理和志愿填报辅助平台。系统基于Django框架开发,采用现代化的Web技术,为教育工作者提供高效、安全、便捷的学生管理解决方案。 ## 📋 系统概述 ### 🎯 系统定…...
