STL之vector篇(上)还在为学习vector而感到烦恼吗?每次做算法题都要回忆很久,不如来看看我的文章,精简又易懂,帮你快速掌握vector的相关用法

文章目录

      • 1. vector的介绍
        • 1.1 基本特性
        • 1.2 主要操作
        • 1.3 注意事项
      • 2. vector的使用
        • 2.1 构造函数
        • 2.2 赋值操作
        • 2.3 迭代器
        • 2.4 容量和大小
        • 2.5 访问元素
        • 2.6 修改容器
        • 2.7 其他操作
      • 3. vector的迭代器失效问题
        • 3.1 迭代器失效的常见情况
        • 3.2 迭代器失效的避免策略


1. vector的介绍

std::vector是C++标准模板库(STL)中的一个非常重要和常用的容器。它提供了一种动态数组的功能,即可以在运行时根据需要自动调整其大小以存储元素。与普通的C数组相比,std::vector提供了更多的灵活性和安全性。

1.1 基本特性
  • 动态大小std::vector能够根据需要自动增长或缩小其存储空间,以存储更多的元素或释放不再需要的内存。
  • 随机访问:支持通过索引(下标)直接访问任意位置的元素,时间复杂度为O(1)。
  • 连续存储:在物理内存中,std::vector的元素是连续存储的,这意味着它可以像普通数组一样被高效地遍历和访问。
  • 类型安全std::vector是模板类,可以在声明时指定存储元素的类型,从而保证了类型安全。
1.2 主要操作
  • 构造函数:用于创建std::vector对象,可以指定初始大小、初始值或从一个已有的范围(如另一个vector、数组等)初始化。
  • 赋值操作:可以将一个std::vector的内容赋值给另一个同类型的vector
  • 迭代器:提供了正向迭代器和反向迭代器,用于遍历vector中的元素。
  • 容量和大小:可以查询vector的当前大小(即存储的元素数量)和容量(即当前分配的存储空间大小)。还可以请求减少容量以匹配实际大小(shrink_to_fit),但这不是强制性的。
  • 访问元素:可以通过索引(下标)或成员函数(如atfrontback)访问vector中的元素。注意,使用索引访问时要确保索引在有效范围内,否则可能导致未定义行为;而at成员函数在索引越界时会抛出异常。
  • 修改容器:支持在vector的末尾添加或移除元素(push_backpop_back),在指定位置插入或移除元素(inserterase),以及通过resize改变vector的大小。
  • 其他操作:如交换两个vector的内容(swap),清空vectorclear)等。
1.3 注意事项
  • vector需要增加其存储空间以存储更多元素时,它通常会分配一个更大的连续内存块,并将旧元素复制到新位置。这个过程可能会导致迭代器、指针和引用失效,因为它们可能指向了旧的内存位置。然而,vector提供的end()迭代器在重新分配后仍然是有效的,尽管它不再指向任何元素。
  • 访问vector的元素时要确保索引在有效范围内,否则可能会导致未定义行为。使用at成员函数可以避免这个问题,但会牺牲一些性能。
  • 在某些情况下,如果知道vector的大致大小或最大大小,可以在创建时预留足够的空间(使用reserve成员函数),以减少重新分配的次数,从而提高性能。

总的来说,std::vector是C++中非常强大和灵活的容器之一,它结合了数组的高效访问和动态数组的动态大小调整能力,是处理动态数据集合时的首选容器之一。

2. vector的使用

vector(向量)是C++标准模板库(STL)中常用的动态数组容器之一,提供了丰富的接口来管理元素集合。以下是vector的一些常用接口介绍:

2.1 构造函数
  • vector<T>():创建一个空的vector。
  • vector<T>(size_type count, const T& value):创建包含count个值为value的元素的vector。
  • vector<T>(const vector<T>& other):复制另一个vector。
  • vector<T>(initializer_list<T> init):使用初始化列表创建vector。
  • vector<T>(InputIt first, InputIt last):创建一个vector,其元素由范围[first, last)内的元素初始化。
#include <vector>  
#include <iostream>  int main() {  // 创建一个空的vector  std::vector<int> emptyVec;  // 创建一个包含5个0的vector  std::vector<int> vec5(5, 0);  // 使用初始化列表创建vector  std::vector<int> initListVec = {1, 2, 3, 4, 5};  // 复制构造函数  std::vector<int> copyVec(vec5);  // 迭代器范围构造函数  std::vector<int> rangeVec(initListVec.begin(), initListVec.end());  // 打印复制和范围构造的vector,以验证它们的内容  for (int n : copyVec) {  std::cout << n << " ";  }  std::cout << "\n";  for (int n : rangeVec) {  std::cout << n << " ";  }  std::cout << std::endl;  return 0;  
}
2.2 赋值操作
  • operator=:将一个vector赋值给另一个vector。
  • assign(InputIt first, InputIt last):用范围[first, last)内的元素替换当前vector的内容。
  • assign(size_type count, const T& value):用count个值为value的元素替换当前vector的内容。
#include <vector>  
#include <iostream>  int main() {  std::vector<int> vec1 = {1, 2, 3};  std::vector<int> vec2;  // 赋值操作  vec2 = vec1;  // 使用assign  vec2.assign(3, 42); // vec2现在包含3个42  // 打印vec2以验证赋值和assign操作  for (int n : vec2) {  std::cout << n << " ";  }  std::cout << std::endl;  return 0;  
}
2.3 迭代器
  • begin():返回指向第一个元素的迭代器。
  • end():返回指向最后一个元素后面的位置的迭代器。
  • rbegin()rend():分别返回指向最后一个元素和第一个元素前面的位置的逆向迭代器。
  • cbegin()cend():与begin()end()类似,但返回的是const迭代器,即不能通过这些迭代器修改vector中的元素。
#include <vector>  
#include <iostream>  int main() {  std::vector<int> vec = {10, 20, 30, 40, 50};  // 使用迭代器遍历vector  for (std::vector<int>::iterator it = vec.begin(); it != vec.end(); ++it) {  std::cout << *it << " ";  }  std::cout << "\n";  // 使用反向迭代器  for (std::vector<int>::reverse_iterator rit = vec.rbegin(); rit != vec.rend(); ++rit) {  std::cout << *rit << " ";  }  std::cout << std::endl;  return 0;  
}
2.4 容量和大小
  • size():返回vector中的元素数量。
  • max_size():返回vector可以容纳的最大元素数量(这是一个理论上的上限,实际使用中很少会达到)。
  • capacity():返回vector当前分配的存储容量。
  • empty():如果vector为空,则返回true,否则返回false。
  • reserve(size_type new_cap):请求vector容器的存储空间至少足够容纳new_cap个元素。这有助于减少重新分配的次数,提高性能。
  • shrink_to_fit():将vector的capacity缩小为与其size相等,但这只是一个请求,编译器可能会忽略它。
#include <vector>  
#include <iostream>  int main() {  std::vector<int> vec;  // 添加元素以改变容量和大小  vec.push_back(1);  vec.push_back(2);  std::cout << "Size: " << vec.size() << ", Capacity: " << vec.capacity() << std::endl;  // reserve更多容量  vec.reserve(10);  std::cout << "Reserved capacity: " << vec.capacity() << std::endl;  // shrink_to_fit(注意:这可能不会总是减少容量)  vec.shrink_to_fit();  // 注意:shrink_to_fit后容量可能不变,因为它是一个请求  std::cout << "Shrunk capacity (may not be reduced): " << vec.capacity() << std::endl;  return 0;  
}
2.5 访问元素
  • operator[]:通过下标访问指定位置的元素(不进行范围检查)。
  • at(size_type pos):访问指定位置的元素,并进行范围检查。如果位置超出范围,将抛出std::out_of_range异常。
  • front():返回第一个元素的引用。
  • back():返回最后一个元素的引用。
  • data():返回指向底层数据的指针(以T*类型)。
#include <vector>  
#include <iostream>  int main() {  std::vector<int> vec = {10, 20, 30, 40, 50};  // 访问元素  std::cout << "First element: " << vec[0] << std::endl;  std::cout << "Last element: " << vec.back() << std::endl;  try {  // 尝试访问超出范围的元素(使用at会抛出异常)  std::cout << "Out of range element: " << vec.at(10) << std::endl;  } catch (const std::out_of_range& e) {  std::cout << "Caught exception: " << e.what() << std::endl;  }  return 0;  
}
2.6 修改容器
  • push_back(const T& value):将元素添加到vector的末尾。
  • pop_back():移除vector的最后一个元素。
  • emplace_back(Args&&... args):在vector的末尾就地构造一个元素,避免了额外的拷贝或移动操作。
  • insert(iterator pos, const T& value):在指定位置插入元素。
  • erase(iterator pos):移除指定位置的元素。
  • erase(iterator first, iterator last):移除范围[first, last)内的元素。
  • clear():移除所有元素,但不改变capacity。
  • resize(size_type count):改变vector的大小,如果新大小大于当前大小,则新元素将被默认构造;如果新大小小于当前大小,则超出的元素将被销毁。
  • resize(size_type count, const T& value):改变vector的大小,并用value值初始化新的元素。
#include <vector>  
#include <iostream>  int main() {  std::vector<int> vec = {1, 2, 3, 4, 5};  // 修改容器  vec.push_back(6); // 在末尾添加元素  vec.pop_back(); // 移除末尾元素  vec.insert(vec.begin(), 0); // 在开头插入元素  vec.erase(vec.begin() + 2); // 移除指定位置的元素  // 使用resize  vec.resize(3); // 缩小vector的大小,超出部分的元素被销毁  vec.resize(5, 10); // 扩大vector的大小,新元素初始化为10  // 打印修改后的vector  for (int n : vec) {  std::cout << n << " ";  }  std::cout << std::endl;  return 0;  
}
2.7 其他操作
  • swap(vector<T>& other):交换两个vector的内容。
#include <vector>  
#include <iostream>  int main() {  std::vector<int> vec1 = {1, 2, 3};  std::vector<int> vec2 = {4, 5, 6};  // 交换vector  vec1.swap(vec2);  // 打印以验证交换  for (int n : vec1) {  std::cout << n << " ";  }  std::cout << "\n";  for (int n : vec2) {  std::cout << n << " ";  }  std::cout << std::endl;  return 0;  
}

3. vector的迭代器失效问题

在C++中,std::vector的迭代器失效问题是一个重要的概念,它主要发生在vector的容量发生变化时。迭代器失效意味着迭代器不再指向有效的内存位置,如果此时尝试通过失效的迭代器访问或修改元素,程序的行为将是未定义的。

3.1 迭代器失效的常见情况
  1. 重新分配:当vector需要增加其存储容量以存储更多元素时(通常是因为调用了push_backinsert等操作,并且当前容量不足以容纳更多元素),它可能会重新分配一个更大的内存块,并将旧元素复制到新位置。这个过程中,所有指向旧内存块的迭代器、指针和引用都会失效。
  2. 删除元素:虽然删除元素(如使用erase)不会导致整个vector的重新分配,但被删除元素之后的所有迭代器、指针和引用都会失效,因为它们不再指向有效的元素。注意,erase方法会返回一个指向被删除元素之后元素的迭代器,这可以用来继续迭代。
3.2 迭代器失效的避免策略
  1. 使用成员函数返回的新迭代器:在删除元素时,使用erase方法返回的迭代器继续迭代。这个迭代器指向被删除元素之后的元素,因此是有效的。
  2. 预留空间:如果可能,使用reserve成员函数提前为vector预留足够的空间。这样可以减少重新分配的次数,从而降低迭代器失效的风险。但是,请注意,reserve不会改变vector的大小(即存储的元素数量),只是改变其容量。
  3. 使用标准算法:当需要在vector中执行复杂的操作时(如排序、查找、删除等),考虑使用标准库提供的算法。这些算法通常设计为与迭代器一起工作,并且能够处理迭代器失效的情况(尽管在某些情况下,如排序,可能需要使用额外的缓冲区来避免迭代器失效)。
  4. 避免在迭代过程中修改vector的大小:在遍历vector时,尽量避免修改其大小(除非你能确保这种修改不会导致迭代器失效,例如只在vector的末尾添加元素)。如果需要频繁地修改vector的大小,并且同时需要遍历它,考虑使用其他数据结构(如listdeque),它们在设计上更加灵活,可以容忍更多的修改而不会导致迭代器失效。

【总结】

std::vector的迭代器失效是一个需要开发者注意的问题。了解何时以及如何避免迭代器失效对于编写健壮、可维护的C++代码至关重要。通过预留空间、使用标准算法和避免在迭代过程中修改vector的大小,可以大大降低迭代器失效的风险。

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

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

My_string 运算符重载,My_stack

思维导图 将My_string类中的所有能重载的运算符全部进行重载 、[] 、>、<、、>、<、! 、&#xff08;可以加等一个字符串&#xff0c;也可以加等一个字符&#xff09;、输入输出(<< 、 >>) My_string my_string.h #ifndef MY_STRING_H #define MY_…

【Web】初识Web和Tomcat服务器

目录 前言 一、认识web 1. 软件架构模式 2. web资源 3. URL请求路径&#xff08;统一资源定位符&#xff09; 二、Tomcat服务器 1. 简介 2. tomcat服务器的目录结构 3.使用tomcat服务器启动失败的常见原因 3.1 端口冲突 3.2 jdk环境变量配置出错 三、使用Tomcat发布…

重塑教育未来:数字教学与智能知识库的深度融合

在当今这个信息爆炸的时代&#xff0c;教育作为推动社会进步与发展的重要基石&#xff0c;正经历着前所未有的变革。随着科技的飞速发展&#xff0c;数字教学与智能知识库作为两大核心驱动力&#xff0c;正携手并进&#xff0c;共同塑造着教育的全新面貌。本文旨在探讨数字教学…

【Docker】Docker快速入门

Docker学习笔记 一、Docker概述 为什么会出现Docker? 安卓开发流程&#xff1a;apk(java开发的)发布到应用商店&#xff0c;用户安装apk即可使用。 后端开发流程&#xff1a; jar(java开发的)带上环境发布到Docker仓库&#xff0c;用户从Docker仓库拉取镜像并部署。 总结…

Java_Day05学习

Object类被子类经常重写的方法 方法说明toString()返回当前对象本身的有关信息&#xff0c;按字符串对象返回equals()比较两个对象是否是同一个对象&#xff0c;是则返回****truehashCode()返回该对象的哈希代码值getClass()获取当前对象所属的类信息&#xff0c;返回Class对象…

TAPD多类别需求管理

本文档将介绍&#xff1a;什么是 TAPD 多类别需求管理&#xff0c;以及如何配置或创建新的需求类别。 一、概述 在研发管理过程中&#xff0c;团队经常会遇到规模扩张、不同特性团队间研发模式差异化大等问题。以上问题导致团队中的需求无法进行统一管理。为解决上述情况&…

《关键跃升读书笔记》11

协作&#xff1a; 怎么解决“容忍⿊”这类问题&#xff1f;我们要重新理解“⽂化”。⼈类⽂化、企 业⽂化&#xff0c;都是为了让⼈们更好地协作。 再⼩的公司&#xff0c;再⼩的团队&#xff0c;都是⼀个共同协作体&#xff0c;就像整个⼈类社会 是共同协作体。理解了⼈类社会…

卷积的理解和应用

妈妈说&#xff0c;生活就像一盒各种口味的巧克力&#xff0c;你永远不知道下一块是什么。 我说生活像这个棒棒糖。不同口味&#xff0c;方向相反的味道一路走一路相伴&#xff0c;衰老和成长缠绕在一起&#xff0c;成了最终的滋味。 一、 卷积的直觉 这一生…

菱形继承的类对父类的初始化、组合、多态、多态的原理等的介绍

文章目录 前言一、菱形继承的类对父类的初始化二、组合三、 多态1. 构成多态2. 虚函数3. 虚函数的重写4. 虚函数重写的两个例外1. 协变2. 析构函数的重写 5. C11 final 和 override1. final2. override 6. 设计不想被继承的类7. 重载、覆盖&#xff08;重写&#xff09;、 隐藏…

YOLOv8——测量高速公路上汽车的速度

引言 在人工神经网络和计算机视觉领域&#xff0c;目标识别和跟踪是非常重要的技术&#xff0c;它们可以应用于无数的项目中&#xff0c;其中许多可能不是很明显&#xff0c;比如使用这些算法来测量距离或对象的速度。 测量汽车速度基本步骤如下&#xff1a; 视频采集&#x…

JVM的基本组成

一、JDK\JRE\JVM JDK: 全称 "Java Development Kit" &#xff0c;Java 开发工具包&#xff0c;提供 javac 编译器、jheap、jconsole 等监控工具;JRE: 全称"Java Runtime Environment"&#xff0c;Java 运行环境&#xff0c;提供Class Library 核心类库 JV…

同等学力英语用什么app背单词

同等学力申硕的意义和作用 授予同等学力人员硕士学位是国家为同等学力人员开辟的获得学位的渠道&#xff0c;对于在职人员业务素质的提高和干部队伍建设起到积极作用。它为那些没有传统学历背景但具有相应学术水平的人员提供了获取学位的机会&#xff0c;有助于提升他们的职业竞…

WebSocket实现在线聊天室

项目实现源码&#xff1a; 前端源码 后端源码 1.常见的消息推送方式 1.1 轮询 1.1.1 轮询的概念 客户端以固定的事件间隔&#xff08;例如每秒或几分钟&#xff09;向服务器发送HTTP请求&#xff0c;服务器收到请求后&#xff0c;处理请求并返回数据给客户端 轮询具体实现htt…

POI获取模板文件,替换数据横纵动态表格、折线图、饼状图、折线饼状组合图

先说几个关键的点 pom.xml依赖 <dependency><groupId>commons-io</groupId><artifactId>commons-io</artifactId><version>2.11.0</version> </dependency> <dependency><groupId>com.deepoove</groupId>&…

鸭脖变“刺客”,啃不起了

撰文&#xff5c;ANGELICA 编辑&#xff5c;ANGELICA 审核&#xff5c;烨 Lydia 声明&#xff5c;图片来源网络。日晞研究所原创文章&#xff0c;如需转载请留言申请开白。 你有多久没吃卤味了&#xff1f; 2020年之后&#xff0c;人们对于几大卤味巨头的关注度正在下降。 …

【MySQL 04】数据类型

目录 1.数据类型分类 2.数值类型 2.1 tinyint 类型 2.2 bit类型 2.3 float类型 2.4decimal 3.字符串类型 3.1 char类型 3.2 varchar类型 4.日期和时间类型 6. enum和set类型 6.1.enum和set类型简介&#xff1a; 6.2.enum和set的一般使用方法 6.3.用数字的方式…

业务数据批量插入数据库实践

业务数据如何存储一直以来都是项目开发中的一个比较重要的话题。我们要从资源的利用率&#xff0c;业务场景和技术实现多个方面考虑存储的问题。“抛开业务谈技术就是耍流氓”&#xff0c;所有技术架构都要站在实际的业务场景中分析。比如个人端的产品&#xff0c;这种就属于读…

fiddler抓包11_列表显示服务器IP (配置文件)

请求列表默认不显示服务器IP字段&#xff0c;也无法从定制列窗口添加&#xff0c;可以修改CustomRules.js实现。 ① 菜单栏“Rules”&#xff08;规则&#xff09; - “Customize Rules...”&#xff08;自定义规则&#xff09;&#xff0c;打开CustomRules.js文件。 &#xf…

基于stm32的跑步机控制系统设计-设计说明书

设计摘要&#xff1a; 随着人们对健康和健身的关注增加&#xff0c;跑步机逐渐成为室内健身的主要设备之一。本文提出了一种基于STM32的跑步机控制系统设计&#xff0c;旨在实现对跑步机的运行速度、倾斜角度和运动模式等参数的精确控制&#xff0c;提供更好的健身体验。 首先…