C++----STL(vector)

news2025/2/24 11:07:45

vector的介绍

vector的文档介绍:cplusplus.com/reference/vector/vector/

1.基本概念

  • 简单来说,vector是表示可以改变大小的数组的顺序容器。
  • 使用连续的存储位置来存储元素,因此可以通过常规指针的偏移量来高效访问。

2.内部机制

  • vector内部使用动态分配的数组来存储元素。
  • 插入新元素时,数组可能需要重新分配以增大尺寸,这是一项相对昂贵的任务。为避免频繁重新分配,vector可能会分配一些额外的存储空间以适应可能的增长。
  • vector的实际容量可能大于存储元素所需的空间(即大小)。

3.内存管理

  • 库采用不同的增长策略来平衡内存使用和重新分配。
  • 重新分配通常只在大小以对数增长间隔发生时进行,以提供摊还常数时间复杂度的插入操作(如push_back)。
  • 与数组相比,向量消耗更多内存,但具备管理和动态增长存储的能力。

4.vector的原型

vector的使用

vector 学习时一定要学会查看文档 vector 在实际中非常的重要,在实际中我们熟悉常见的接口就可以,下面列出了哪些接口是要重点掌握的。(vector与string都基于顺序表实现,因此接口相似。如果已经掌握string用法的,理解vector会更容易。后续讲解将简要提及相似之处,重点介绍vector的特有特性和用法。)

迭代器

begin()

  • 功能说明:返回一个指向vector第一个元素的迭代器。
  • 注意:有可读可写和只读两种迭代器。

rbegin()

  • 功能说明:返回一个指向vector最后一个元素的迭代器。
  • 同样有可读可写和只读两种。(下面讲解的迭代器都如此)

end()

  • 功能说明:返回一个指向vector最后一个元素之后位置的迭代器。

rend()

  • 功能说明:返回一个指向vector第一个元素之前位置的反向迭代器。

构造函数

1.使用填充构造

//原型:explicit vector (size_type n, const value_type& val = value_type(),const allocator_type& alloc = allocator_type());
vector<int> v1(10, 1);//创建一个包含10个整数的vector,每个整数初始化为1
vector<string> v2(10, "***");//创建一个包含10个字符串的vector,每个字符串初始化为"***"

2.使用迭代器范围构造

//原型:template <class InputIterator> vector (InputIterator first, InputIterator last,const allocator_type& alloc = allocator_type());
vector<int> v3(v1.begin(), v1.end());//使用v1的迭代器范围构造v3

string str("hello world");
vector<char> v4(str.begin(), str.end());//使用字符串string的迭代器范围构造字符vector v4

int a[] = {16, 2, 77, 29};
vector<int> v5(a, a + 4); // 使用数组a的迭代器范围构造v5

补充:配合sort函数的使用进行升序和降序排序

sort(v5.begin(), v5.end()); // 使用默认比较器(less<int>)对v5进行升序排序
sort(v5.begin(), v5.end(), greater<int>()); // 使用greater<int>比较器对v5进行降序排序
sort(v5.rbegin(),v5.rend()); // 使用反向迭代器对v5进行降序排序
sort(str.begin(), str.end()); // 对字符串str进行字典序排序
sort(a, a + 4); // 对数组a进行排序

容量相关的操作

size()

  • 功能说明:返回vector中当前元素的个数。

max_size()

  • 功能说明:返回vector中能容纳的最大元素个数。

capacity()

  • 功能说明:返回vector在当前情况下能容纳的元素个数。

reserve()

  • 功能说明:扩容。
  • 注意:reserve不改变vector的大小(即size()的值),也不初始化新分配的内存。
  • 易错展示:
    vector<int> v1;
    v1.reserve(10);
    for (size_t i = 0; i < 10; i++) {
        v1[i] = i; // 错误:v1.size()仍为0,访问v1[i]是未定义行为。
    }
  •  正确做法:
    vector<int> v1;
    v1.reserve(10);
    for (size_t i = 0; i < 10; i++) {
        v1.push_back(i); // 正确:使用push_back添加元素并初始化。
    }

resize()

  • 功能说明:扩容+初始化
    vector<int> v1;
    v1.resize(10); // 改变大小为10,新元素初始化为0(对于int类型)。
    for (size_t i = 0; i < 10; i++) {
        v1[i] = i; // 正确:v1的大小已经是10。
    }

empty()

  • 功能:检查vector是否为空。如果vector的大小为0,则返回true;否则返回false

shrink_to_fit()

  • 功能:请求移除vector中多余的容量。调用后,vector的容量将被调整为当前大小(即size()的值),前提是这不会增加内存分配的总大小。

元素访问

operator[]

  • 功能:通过索引访问vector中的元素。
  • 注意:不进行边界检查,如果索引超出范围,则行为未定义,可能导致程序崩溃或数据损坏)
    vector<int> v = {1, 2, 3};
    int first = v[0]; // 访问第一个元素,值为1。

at()

  • 功能:通过索引访问vector中的元素。
  • 与operator[]不同,at会进行边界检查。如果索引超出范围,则抛出std::out_of_range异常。
    vector<int> v = {1, 2, 3};
    int second = v.at(1); // 访问第二个元素,值为2。

data()

  • 功能说明:返回指向vector中第一个元素的指针(类型为T*,其中T是vector存储的元素类型)。如果vector为空,则返回空指针。
  • 类似于string中的c_str()方法,但data返回的是可修改的指针。
    vector<int> v = {1, 2, 3};
    int* ptr = v.data();
    cout << ptr[0] << " " << ptr[1] << " " << ptr[2] << endl; // 输出:1 2 3

修改操作

push_back()

  • 功能:在vector末尾添加一个元素。

pop_back()

  • 功能:移除vector末尾的元素.

insert()

  • 功能:在指定位置插入一个或多个元素。
    int a[] = { 16,2,77,29,3,33,43,3,2,3,3,2 };
	vector<int> v1(a, a + sizeof(a)/sizeof(int));

	// 头插 
	v1.insert(v1.begin(), 100);

erase()

  • 功能:移除指定位置的元素或一段元素。
    int a[] = { 16,2,77,29,3,33,43,3,2,3,3,2 };
	vector<int> v1(a, a + sizeof(a)/sizeof(int));
	
	// 头删
	v1.erase(v1.begin());

	// 删除第3个数据
	v1.erase(v1.begin() + 2);

注意:std::vector中使用eraseinsert后迭代器失效的情况

在C++的STL(标准模板库)中,vector是一个动态数组,其内存可以重新分配和移动。因此,当你对vector进行eraseinsert,可能会导致指向vector元素的迭代器失效。失效的迭代器不能再被用来访问vector中的元素,因为这样做会导致未定义行为。

下面的代码例子,展示了在std::vector中使用erase后迭代器失效的情况:

#include <iostream>
#include <vector>
using namespace std;

int main() {
    vector<int> vec = { 1, 2, 3, 4, 5 };

    // 创建一个指向vector中第三个元素的迭代器
    auto it = vec.begin() + 2; // 指向3

    // 输出迭代器指向的值
    cout << "Before erase: " << *it << endl; // 输出: 3

    // 删除迭代器指向的元素
    vec.erase(it);// 此时it已经失效

    for (auto e : vec) {
        cout << e << " ";
    }
    cout << endl;

    // 下面的代码是未定义行为,因为it已经失效
    // cout << "After erase (undefined behavior): " << *it << endl; // 不要这样做

    return 0;
}

正确做法:

#define _CRT_SECURE_NO_EARNINGS 1
#include <iostream>
#include <vector>
using namespace std;

int main() {
    vector<int> vec = { 1, 2, 3, 4, 5 };

    // 创建一个指向vector中第三个元素的迭代器
    auto it = vec.begin() + 2; // 指向3

    // 输出迭代器指向的值
    cout << "Before erase: " << *it << endl; // 输出: 3

    // 删除迭代器指向的元素
    it = vec.erase(it);// 我们可以获取删除元素后的下一个元素

    for (auto e : vec) {
        cout << e << " ";
    }
    cout << endl;

    cout << "After erase (undefined behavior): " << *it << endl; // 此时的it指向被删除的位置的下一个元素4

    return 0;
}

下面的代码例子,展示了在std::vector中使用insert后迭代器失效的情况:

#define _CRT_SECURE_NO_EARNINGS 1
#include <iostream>
#include <vector>
using namespace std;

int main() {
    vector<int> vec = { 1, 2, 3, 4, 5 };

    vector<int>::iterator it = vec.begin() + 2;
    cout << "it: " << *it << endl;

    // 插入新元素
    vec.insert(vec.begin(), 10); // 在开头插入10

    for (auto e : vec) {
        cout << e << " ";
    }
    cout << endl;

    // 所有指向vec元素的迭代器(包括end()之前的迭代器)都可能失效
    // 下面的代码同样是未定义行为,因为之前的迭代器(即使它们没有直接指向被插入的位置)也可能不再有效
    // cout << "After insert (undefined behavior): " << *it << endl; // 这是错误的

    return 0;
}

正确做法:

#define _CRT_SECURE_NO_EARNINGS 1
#include <iostream>
#include <vector>
using namespace std;

int main() {
    vector<int> vec = { 1, 2, 3, 4, 5 };

    vector<int>::iterator it = vec.begin() + 2;
    cout << "it: " << *it << endl;

    // 插入新元素
    vec.insert(vec.begin(), 10); // 在开头插入10

    for (auto e : vec) {
        cout << e << " ";
    }
    cout << endl;

    // 正确的做法是重新获取迭代器
    it = vec.begin() + 2;
    cout << "After insert (undefined behavior): " << *it << endl; 

    return 0;
}

swap()

  • 功能:交换两个vector的内容。

clear()

  • 功能:移除vector中的所有元素,使其变为空容量。

assign()

  • 功能:清楚vector中的值,并用新值重新赋值。
    v1.assign(10, 1); // 将v1重新赋值为10个1

补充:find在 vector 中的应用

vector中的find:vector不像string在成员函数中直接提供了find(),而是通过<algorithm>头文件中的find函数,用于查找元素的位置。

#include<iostream>
#include<algorithm>
using namespace std;

int main()
{
    int a[] = { 16,2,77,29,3,33,43,3 };
	vector<int> v1(a, a + sizeof(a)/sizeof(int));
    auto pos = find(v1.begin(), v1.end(), 3); // 在v1中查找元素3

    // 删除第3个数据
	v1.erase(v1.begin()+2);

    return 0;
}

迭代器失效问题

#include<iostream>
#include<algorithm>
using namespace std;

int main()
{
    int a[] = { 16,2,77,29,3,33,43,3 };
	vector<int> v1(a, a + sizeof(a)/sizeof(int));
    auto pos = find(v1.begin(), v1.end(), 3); // 在v1中查找元素3

    // 删除所有的3 -- 涉及迭代器失效!后面解决
	while(pos != v1.end()){
      v1.erase(pos);
      pos = find(v1.begin(), v1.end(), 3); // 可能导致无限循环,因为pos可能已失效
    }

    return 0;
}

原因:vector在insert或erase操作后,可能会重新分配内存(尤其是当容量不足时),导致所有指向该vector的迭代器失效。

解决:

    while((pos = find(v1.begin(), v1.end(), 3)) != v1.end())
    {
        v1.erase(pos);
    }

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

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

相关文章

Airflow:BranchOperator实现动态分支控制流程

Airflow是用于编排复杂工作流的开源平台&#xff0c;支持在有向无环图&#xff08;dag&#xff09;中定义、调度和监控任务。其中一个关键特性是能够使用BranchOperator创建动态的、有条件的工作流。在这篇博文中&#xff0c;我们将探索BranchOperator&#xff0c;讨论它是如何…

rocketmq-MQClientInstance-单进程多生产者组多消费者组的实例模型

多生产者组多消费者组的思考 思考下。当一个client&#xff0c;订阅多个consumergroup、多个productgroup时。此时进程的线程模型是如何的&#xff1f; 之前文章有分析到。消费者组&#xff0c;是有多个线程去共同协作的。 假设订阅2个consumergroup&#xff0c; 线程数量是2倍…

nuxt3项目打包部署到服务器后配置端口号和开启https

nuxt3打包后的项目部署相对于一般vite打包的静态文件部署要稍微麻烦一些&#xff0c;还有一个主要的问题是开发环境配置的.env环境变量在打包后部署时获取不到&#xff0c;具体的解决方案可以参考我之前文章 nuxt3项目打包后获取.env设置的环境变量无效的解决办法。 这里使用的…

Class ‘com.xxx.xxx‘ not found in module ‘xxxx‘ 解决方法

目录 前言1. 问题所示2. 原理分析3. 解决方法前言 🤟 找工作,来万码优才:👉 #小程序://万码优才/r6rqmzDaXpYkJZF 1. 问题所示 启动项目的时候,出现如下Bug: Class ‘com.xxx.xxx‘ not found in module ‘xxxx‘截图如下: 2. 原理分析 Java 项目中引用的类未能被正…

ngrok同时配置多个内网穿透方法

一、概要 ngrok可以用来配置免费的内网穿透&#xff0c;启动后就可以用外网ip:端口访问到自己计算机的某个端口了。 可以用来从外网访问自己的测试页面&#xff08;80、8080&#xff09;、ftp文件传输&#xff08;21&#xff09;、远程桌面&#xff08;3389&#xff09;等。 …

OGG 19C 集成模式启用DDL复制

接Oracle19C PDB 环境下 OGG 搭建&#xff08;PDB to PDB&#xff09;_cdb架构 配置ogg-CSDN博客&#xff0c;给 pdb 环境 ogg 配置 DDL 功能。 一个报错 SYShfdb1> ddl_setup.sqlOracle GoldenGate DDL Replication setup scriptVerifying that current user has privile…

【计算机网络】- 应用层HTTP协议

目录 初识HTTP 什么是HTTP 版本 HTTPS 模型 HTTP抓包工具 为什么使用 抓包工具的下载 下载后的重要操作 Fiddler的使用 HTTP请求与响应的基本格式 HTTP请求基本格式​编辑 HTTP响应基本格式 协议格式总结❗️❗️❗️​编辑 HTTP 详解 认识 URL URL基本格式 …

基于SpringBoot+Vue的旅游管理系统【源码+文档+部署讲解】

系统介绍 基于SpringBootVue实现的旅游管理系统采用前后端分离架构方式&#xff0c;系统设计了管理员、用户两种角色&#xff0c;系统实现了用户登录与注册、个人中心、用户管理、景点信息管理、订票信息管理、用户评价管理、景点咨询、轮播图管理等功能。 技术选型 开发工具…

Agent群舞,在亚马逊云科技搭建数字营销多代理(Multi-Agent)(下篇)

在本系列的上篇中&#xff0c;小李哥为大家介绍了如何在亚马逊云科技上给社交数字营销场景创建AI代理的方案&#xff0c;用于社交动态的生成和对文章进行推广曝光。在本篇中小李哥将继续本系列的介绍&#xff0c;为大家介绍如何创建主代理&#xff0c;将多个子代理挂载到主代理…

【Ubuntu】安装SSH启用远程连接

【Ubuntu】安装OpenSSH启用远程连接 零、安装软件 使用如下代码安装OpenSSH服务端&#xff1a; sudo apt install openssh-server壹、启动服务 使用如下代码启动OpenSSH服务端&#xff1a; sudo systemctl start ssh贰、配置SSH&#xff08;可跳过&#xff09; 配置文件 …

后端开发Web

Maven Maven是apache旗下的一个开源项目&#xff0c;是一款用于管理和构建java项目的工具 Maven的作用 依赖管理 方便快捷的管理项目依赖的资源&#xff08;jar包&#xff09;&#xff0c;避免版本冲突问题 统一项目结构 提供标准、统一的项目结构 项目构建 标准跨平台(…

STM32项目分享:智能宠物喂食系统(升级版)

目录 一、前言 二、项目简介 1.功能详解 2.主要器件 三、原理图设计 四、PCB硬件设计 PCB图 五、程序设计 六、实验效果 七、资料内容 项目分享 一、前言 项目成品图片&#xff1a; 哔哩哔哩视频链接&#xff1a; https://www.bilibili.com/video/BV19hmMY6ErU…

【程序化广告】相关技术(RTB竞价原理、Cookie映射流程、数据统计原理、程序化创意、防作弊方法)

上一篇介绍了【程序化广告】广告投放流程/漏斗/要素/策略/指标&#xff0c;本篇介绍一下程序化广告所使用到的相关技术&#xff0c;包括RTB竞价原理、Cookie映射流程、数据统计原理、程序化创意、防作弊方法等。 1. RTB竞价原理 1&#xff09;竞价逻辑 用户开启电脑&#xf…

STM32补充——IAP

0 前置知识&#xff1a; FLASH相关内容&#xff1a;前往STM32补充——FLASH STM32三种烧录方式&#xff08;看看就行&#xff09;&#xff1a; 1.ISP&#xff1a;In System Programming&#xff08;在系统编程&#xff09; 执行芯片厂商的 Bootloader 程序进入 ISP 模式&…

Spring Boot中选择性加载Bean的几种方式

说明&#xff1a;用过Spring框架的都知道其自动装配的特性&#xff0c;本文介绍几种选择性加载Bean的方式。Spring自动装配参考以下两篇文章&#xff1a; 基于SpringBoot的三层架构开发&统一响应结果 SpringBoot自动装配原理简单分析 ConditionalOnProperty Conditiona…

AI刷题-策略大师:小I与小W的数字猜谜挑战

问题描述 有 1, 2,..., n &#xff0c;n 个数字&#xff0c;其中有且仅有一个数字是中奖的&#xff0c;这个数字是等概率随机生成的。 Alice 和 Bob 进行一个游戏&#xff1a; 两人轮流猜一个 1 到 n 的数字&#xff0c;Alice 先猜。 每完成一次猜测&#xff0c;主持会大声…

利用Java爬虫获取eBay商品详情:代码示例与教程

在当今的电商时代&#xff0c;获取商品详情数据对于市场分析、价格监控和竞品研究至关重要。eBay作为全球最大的电商平台之一&#xff0c;拥有海量的商品信息。通过Java爬虫技术&#xff0c;我们可以高效地获取这些数据&#xff0c;为商业决策提供支持。本文将详细介绍如何使用…

编译Android平台使用的FFmpeg库

目录 前言 一、编译环境 二、搭建环境 1.安装MSYS2 2.更新系统包 2.1 打开MSYS2 MinGW 64-bit终端&#xff08;mingw64.exe&#xff09; 2.2 更新所有软件包到最新版本 2.3 安装必要的工具和库。 3. 克隆FFmpeg源码 4. 配置编译选项 5. 执行编译 总结 前言 记录学习…

30天开发操作系统 第 17 天 -- 命令行窗口

前言 今天一开始&#xff0c;请大家先回忆一下任务A的情形。在harib13e中&#xff0c;任务A下面的LEVEL中有任务因此FIFO为空时我们可以让任务A进入休眠状态。那么&#xff0c;如果我们并未启动任务B0~ B0~ B2, B2的话&#xff0c;任务A又将会如何呢&#xff1f; 首先&#xf…

阿九的python 爬虫进阶课18.3 学习笔记

文章目录 前言1. 爬取大标题2. 爬取小标题3. 证券栏下的标题4. 某篇文章里的具体内容 前言 网课链接&#xff1a;https://www.bilibili.com/video/BV1kV4y1576b/新浪财经网址&#xff1a;https://finance.sina.com.cn/需先下载库&#xff1a; conda install lxml布置爬取的一…