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

news2024/9/25 2:33:49

文章目录

      • 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.coloradmin.cn/o/2162164.html

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

相关文章

SpringCloud微服务实现服务熔断的实践指南

Spring Cloud是一套分布式系统的微服务框架&#xff0c;它提供了一系列的组件和工具&#xff0c;能够使我们更容易地构建和管理微服务架构。在实际开发中&#xff0c;由于各个服务之间的通信依赖&#xff0c;一旦某个服务出现故障或负载过高&#xff0c;可能会导致整个系统的性…

Growthly Quest 增长工具:助力 Web3 项目实现数据驱动的增长

作者&#xff1a;Stella L (stellafootprint.network) 在瞬息万变的 Web3 领域&#xff0c;众多项目在用户吸引、参与和留存方面遭遇重重难关。Footprint Analytics 推出 Growthly&#xff0c;作为应对这些挑战的全方位解决方案&#xff0c;其中创新性的 Quest&#xff08;任务…

Maya学习笔记:物体的层级关系

文章目录 父子关系设置父子关系同时显示两个大纲视图 组 父子关系 设置父子关系 设置父子物体&#xff1a; 方法1 先选择子物体&#xff0c;按住shift再选中父物体&#xff0c;按P或者G键 方法2 在大纲视图中按住鼠标中间&#xff0c;拖动一个物体到另一个物体上 取消父子关…

HC32F460JETA使用串口DMA循环传输数据时遇到问题,只传输了一次就停止传输,如何解决??

&#x1f3c6;本文收录于《CSDN问答解惑-专业版》专栏&#xff0c;主要记录项目实战过程中的Bug之前因后果及提供真实有效的解决方案&#xff0c;希望能够助你一臂之力&#xff0c;帮你早日登顶实现财富自由&#x1f680;&#xff1b;同时&#xff0c;欢迎大家关注&&收…

物联网实践教程:微信小程序结合OneNET平台MQTT实现STM32单片机远程智能控制 远程上报和接收数据——STM32代码实现篇

STM32代码实现 开启本章节需要完成下方的前置任务&#xff1a; 点击跳转&#xff1a; 物联网实践教程&#xff1a;微信小程序结合OneNET平台MQTT实现STM32单片机远程智能控制 远程上报和接收数据——汇总 目标 1.连接OneNET&#xff1a;STM32使用串口与ESP8266/01s连接发送…

基于Vue3组件封装的技巧分享

本文在Vue3的基础上针对一些常见UI组件库组件进行二次封装&#xff0c;旨在追求更好的个性化&#xff0c;更灵活的拓展&#xff0c;提供一些个人的思路见解&#xff0c;如有不妥之处&#xff0c;敬请指出。核心知识点$attrs,$slots 需求 需求背景 日常开发中&#xff0c;我们经…

PHP判断微信或QQ访问

PHP判断微信或QQ访问 若是微信或者QQ打开&#xff0c;提示图会覆盖网页&#xff0c;但网页功能仍在运行&#xff01; <meta name"viewport" content"initial-scale1, maximum-scale1, user-scalableno, widthdevice-width"><style> .top-gui…

leetcode第169题:多数元素

给定一个大小为 n 的数组 nums &#xff0c;返回其中的多数元素。多数元素是指在数组中出现次数 大于 ⌊ n/2 ⌋ 的元素。 你可以假设数组是非空的&#xff0c;并且给定的数组总是存在多数元素。 示例 1&#xff1a; 输入&#xff1a;nums [3,2,3] 输出&#xff1a;3 示例 …

OpenHarmony(鸿蒙南向)——平台驱动开发【ADC】

往期知识点记录&#xff1a; 鸿蒙&#xff08;HarmonyOS&#xff09;应用层开发&#xff08;北向&#xff09;知识点汇总 鸿蒙&#xff08;OpenHarmony&#xff09;南向开发保姆级知识点汇总~ 持续更新中…… 概述 功能简介 ADC&#xff08;Analog to Digital Converter&…

LOGO设计新革命:5款AI工具让你秒变设计大师(必藏)

大家好&#xff0c;我是Shelly&#xff0c;一个专注于输出AI工具和科技前沿内容的AI应用教练&#xff0c;体验过300款以上的AI应用工具。关注科技及大模型领域对社会的影响10年。关注我一起驾驭AI工具&#xff0c;拥抱AI时代的到来。 你是否曾因设计一个既独特又专业的LOGO而感…

JUC高并发编程2:Lock接口

1 synchronized 1.1 synchronized关键字回顾 synchronized 是 Java 中的一个关键字&#xff0c;用于实现线程间的同步。它提供了一种简单而有效的方式来控制对共享资源的访问&#xff0c;从而避免多个线程同时访问同一资源时可能出现的竞态条件&#xff08;race condition&am…

【Linux网络 —— 网络基础概念】

Linux网络 —— 网络基础概念 计算机网络背景网络发展 初始协议协议分层协议分层的好处 OSI七层模型TCP/IP五层(或四层)模型 再识协议为什么要有TCP/IP协议&#xff1f;什么是TCP/IP协议&#xff1f;TCP/IP协议与操作系统的关系所以究竟什么是协议&#xff1f; 网络传输基本流程…

【openwrt】 libubox组件——ustream

文章目录 ustream 核心数据结构struct ustreamstruct ustream_buf_liststruct ustream_bufstruct ustream_fd ustream 核心APIustream_fd_initustream_uloop_cbustream_fd_read_pendingustream_fill_read ustream_write_pendingustream_writeustream_fd_write ustream 应用示例…

Python画笔案例-059 绘制甩曲彩点动图

1、绘制甩曲彩点动图 通过 python 的turtle 库绘制 甩曲彩点动图,如下图: 2、实现代码 绘制甩曲彩点动图,以下为实现代码: """甩曲彩点动图.py """ import time import turtlecs = [red,orange,

CVPT: Cross-Attention help Visual Prompt Tuning adapt visual task

论文汇总 当前的问题 图1:在VTAB-1k基准测试上&#xff0c;使用预训练的ViT-B/16模型&#xff0c;VPT和我们的CVPT之间的性能和Flops比较。我们将提示的数量分别设置为1、10、20、50,100,150,200。 如图1所示&#xff0c;当给出大量提示时&#xff0c;VPT显示了性能的显著下降…

串口问题汇总:串口发送乱码,重定义使用printf ,输出顺序出错,缓存区思想,串口项目应用

1.c51使用串口出现顺序被覆盖的情况&#xff0c;也就是输出time 最后输出的却是te 这是因为你没有等待上一个数据发送就开始发送下一个数据就会导致数据篡位 2.c51想使用串口重定义使用printf 首先c51是自带stdio.h不需要像32那样点击 include lib选项&#xff0c;你直接改…

力扣958:判断二叉树是否为完全二叉树

给你一棵二叉树的根节点 root &#xff0c;请你判断这棵树是否是一棵 完全二叉树 。 在一棵 完全二叉树 中&#xff0c;除了最后一层外&#xff0c;所有层都被完全填满&#xff0c;并且最后一层中的所有节点都尽可能靠左。最后一层&#xff08;第 h 层&#xff09;中可以包含 …

体制内打工人收藏!5款AI写作工具,助你变成单位笔杆子~

对于初入体制内职场的新手或是日常任务繁重、难以抽身撰写文件的同事们&#xff0c;别再让加班的夜晚成为常态&#xff01;现在&#xff0c;就让我揭秘几个高效公文写作宝库&#xff0c;它们能助你迅速掌握公文写作的精髓&#xff0c;海量素材信手拈来&#xff0c;更有快速成文…

Elasticsearch、ik分词器、elasticsearch-head、Kibana的认识与安装

文章目录 elasticsearch安装elasticsearchIK中文分词器elasticsearch-headkibana elasticsearch Elasticsearch是一个基于Lucene的搜索服务器&#xff0c;也是属于NoSQL阵营的数据库。它提供了一个分布式多用户能力的全文搜索引擎&#xff0c;基于RESTful web接口提供给我们操…

2025年SEO策略:如何优化您的知识库?

如今很多人在遇到问题时都会求助于谷歌。谷歌已经成为提供解决方案不可或缺的工具。作为全球搜索引擎的巨头&#xff0c;拥有大量用户流量。这就是为什么确保您的产品和服务在谷歌搜索结果中排名靠前是至关重要的&#xff0c;如果您想获得更多的客户&#xff0c;SEO是一个非常关…