跳到主要内容
极客日志极客日志面向AI+效率的开发者社区
首页博客GitHub 精选镜像工具UI配色美学隐私政策关于联系
搜索内容 / 工具 / 仓库 / 镜像...⌘K搜索
注册
博客列表
C++算法

C++ vector 容器详解(一)

综述由AI生成介绍 C++ STL 中 vector 容器的基本概念、构造函数、析构函数、赋值运算符重载及常用操作。重点讲解了 vector 与 string 的区别,emplace_back 与 push_back 的性能差异,以及使用算法库 find 函数进行查找和 erase 进行删除的方法。最后通过杨辉三角案例演示了 vector 嵌套的使用场景。

雾岛听风发布于 2026/3/27更新于 2026/5/2826 浏览

1. vector 的简要介绍

在数学/物理领域,vector 的中文意思是'向量'。在 C++ 标准库中,std::vector 是一个动态数组容器,中文常直接称为 vector 容器,也可意译为动态数组。所以由此可见,vector 的底层实际上就是顺序表。

参考 C++ 官方文档,我们可以看到 vector 是 C++ 标准库中定义在 std 标准命名空间下的一个模板类。它的第一个参数 class T,用于指定 vector 容器中存储元素的类型。例如写下 std::vector<int>,此时 T 被实例化为 int 类型,意味着这个 vector 容器用来存储 int 类型的数据;在 std::vector<std::string> 中,T 被实例化为 std::string 类型,容器用来存储字符串。

它的第二个参数 class Alloc = allocator,用于指定内存分配器的类型,负责为 vector 容器分配和释放内存。默认使用 allocator,这是 C++ 标准库提供的默认内存分配器,能够满足大多数情况下对内存管理的需求。不过在一些特殊场景,比如对内存分配有严格控制(像内存池的实现),或者针对特定硬件平台优化内存使用时,可以自定义内存分配器,然后通过这个参数传递给 vector。

对于 vector 这个类里面有以下内容:

Image

我们会发现 vector 和 string 一样,都有构造函数、迭代器、容量、元素访问操作函数、修改操作函数、内存分配操作函数和非成员函数重载。

Image

首先大家一定要明白的一点是,当你想要定义一个 vector 对象,就必须要加上它的类型,因为我们说 vector 实际上是一个顺序表,既然是顺序表它肯定会存储内容,那么内容的类型一定要确定,否则无法调用默认构造函数。

2. vector 的构造函数

Image

大家可以看到 vector 的四个构造函数里面都有一个参数:const allocator_type& alloc = allocator_type()。它有一个缺省值是 allocator_type(),这个缺省值是 C++ 标准库提供的默认内存分配器,负责为 vector 容器分配和释放内存,只有在极少数的情况下我们才会为这个参数传值,因为 C++ 标准库提供的默认内存分配器能满足极大范围场景的使用。在这边大家就可以把它当成没看到。

首先来看第一个构造函数,它是无参构造函数,就相当于默认构造函数。

第二个构造函数当中有一个参数,它有一个缺省值叫 value_type()。这个 value_type() 是 C++ 中'值初始化'(Value Initialization)的语法形式,它会根据 value_type 的类型,生成该类型的默认值。刚刚在 vector 的简要介绍里面提到,如果想要定义一个 vector 对象,就一定要声明这个对象的类型,而这个类型就是 value_type。比如我的代码中写到:vector<int> v1; 此时 value_type 就是 int,然后通过 value_type() 去生成 int 的默认初始值,根据 C++ 的初始化规则,int 的默认初始值就是 0。如果类型是 char,那默认初始值就是 '\0',如果是类型是 double,那默认初始值就是 0.0。所以这个构造函数的作用就是创建构造一个有 n 个 val 的 vector 对象。

第三个构造函数里面的参数是迭代器,并且是 begin 和 end 迭代器。比如有两个 vector 对象,v1 和 v2,写出 v2(v1.begin(), v1.end()),就表示 v2 存储的值是 v1 的从头到尾的内容。其实有点像拷贝构造函数。

而第四个就是最普通的拷贝构造函数。

接下来给大家展示一下代码:

Image

3. vector 的析构函数

Image

4. vector 的赋值运算符重载

Image

5. vector 的遍历和访问操作

因为标准库的 vector 类中本身并没有重载流插入运算符,所以对于我们上面的构造函数,想要把具体存储的内容打印出来,如果直接使用 cout<<v1 的话就会报错。因此我们这里使用 for 循环和访问下标的方式逐个输出元素。

Image

除了使用 for 循环,还可以使用范围 for 和迭代器来简化循环操作:

Image

在这里我展示一个 v2 对象和一个 v3 对象的打印。在这里我们同时也使用了迭代器中的 begin 和 end。所以大家会发现 STL 容器有一个特点就是:详细学习了其中一种容器之后,学习其他的容器就会非常快。vector 有非常多和 string 相似的地方,经过了前面对 string 的详细学习,所以我们对 vector 的讲解的进度就会快很多。

6. vector 的插入操作

首先来看一下 vector 里面不同于 string 类里面的插入:emplace_back。

Image

其实对于 emplace_back 来说它就有点像 push_back 都是在对象末尾插入内容,但有所不同的是,emplace_back 在某些场景下效率会更高一些。

emplace_back 直接在 vector 的末尾内存空间(预留 / 新分配的空间)中就地构造对象。它不需要接收已构造的对象,而是接收对象构造所需的参数列表,然后在 vector 的目标位置直接调用对象的构造函数。即:跳过了临时对象的创建步骤,直接在目标内存地址完成对象构造。

假设我们有一个自定义类 Person,只有带参构造函数 Person(string name, int age)。

使用 push_back 添加对象时,必须先创建一个临时对象,再将其拷贝 / 移动到 vector 中,临时对象随后会被销毁:

使用 emplace_back 时,直接传入构造参数,在 vector 末尾就地构造 Person 对象,全程无临时对象产生:

Image

效率更高的第二点是:对于 push_back:无论传入左值还是右值,都需要额外调用一次「拷贝构造函数」或「移动构造函数」,将对象从原内存(或临时内存)复制 / 移动到 vector 的内存中。

对于 emplace_back:直接在 vector 的目标内存中构造对象,无需额外的拷贝或移动操作,仅调用一次对象的普通构造函数即可完成添加操作,减少了一次构造调用的开销。

总结一下就是:对于内置类型(如 int、char),两者性能差异几乎可以忽略(因为内置类型无复杂构造 / 析构逻辑)。在绝大多数场景下,优先使用 emplace_back,既能提升性能,又能增强兼容性;仅在需要明确传入已构造对象,且无需就地构造时,可使用 push_back。

Image

我们来看一下 vector 中的 insert。string 里面的 insert 函数的作用是:在指定位置插入字符,然后其余的内容向后自动移动。vector 里的 insert 也是一样,但是 vector 的 insert 设计的就简洁了很多,并且我们来看这里面的参数,都有一个 iterator position,表示迭代器的位置,而 string 里的 insert 用的还是字符串的下标。我们来做一下代码演示:

Image

对于 v1 来说,是直接在头部插入一个数字 1。

对于 v2 来说,是在 v2 的头部插入 3 个 2。

对于 v3 来说是在 v3 的头部插入 v2 的从头到尾。

但是按照目前参数的逻辑,只能在头部或者尾部插入。那如果我想要在其他位置插入值的话该怎么办呢?因为我们使用的是迭代器,对于迭代器来说它是一个像指针一样的东西,既然是像指针的话,我们就可以用加减指向的位置来进行操作:

Image

对于 v3 这个 vector 对象来说,它原本自身存储的内容是 4 个 3,现在我调用 insert 函数,在 v3 头部往后两个位置的地方,插入 v2 的从头到尾。这就实现了任意位置插入的操作。

那如果我现在不想要根据下标位置来插入,我想要根据 vector 对象里存储的内容的位置来插入。比如说有一个 vector 对象存储的是 1 2 3 4 5,我想要在 3 后面插入一个 6,那这个时候我应该是要先找到数字 3,然后再进行插入操作,但问题是该怎么找到数字 3 呢?

7. 算法库中的 find 函数

在 string 类当中有一个函数叫 find,可是我们观察一下,在 vector 类中竟然没有 find 函数。其实这是因为 vector 如果想要使用 find 函数,使用的其实是标准算法库中的 find 函数:

Image

算法库里面的 find 函数其实是一个模板,所以不管是 vector 类还是 list 类都可以用这个 find 函数来进行查找内容的操作。那为什么 string 类需要自己创建一个 find 函数,而不用算法库里的 find 函数呢?这是因为对于 string 类的查找比较复杂,它需要从指定位置开始查找等等。

Image

那我们来看一下算法库中 find 函数的样例,因为 find 函数如果没有找到指定内容,就返回要查找的这个数组里面的最后一个内容,如果找到了,就返回找到的这个数字的迭代器。

Image

首先我先创建了一个 vector 对象 v1,然后往 v1 里面依次插入 0 到 9 共 10 个数字。接着调用 find 函数并用 it1 这个变量来接受 find 的返回值。然后再调用 insert 函数在下标为 3 的数字后面插入一个 50。

8. vector 中的删除操作

Image

大家可以看到 erase 的参数也是迭代器的位置,并且还有一个迭代器区间。那么这样的话我们就可以实现指定位置的删除和指定区间的删除。

Image

比如这里我是想要把 v1 首元素往后两个位置的数字,也就是数字 2 给删除。那么调用 erase 函数,让迭代器 begin 往后加 2。要注意的是不管是 vector 还是 string 的 insert 和 erase 函数,因为都涉及到元素的移动,所以会降低效率,要谨慎使用。

9. vector 的关系运算符重载

Image

和 string 一样,vector 也支持关系运算符重载,并且 vector 的比较大小是根据 vector 储存内容依次去比较,而不是直接比较 vector 的长度大小。和 string 的比较大小逻辑类似。

10. vector 和 string 的区别

Image

大家来看这两行代码,我们说 string 的底层是一个数组,vector 的底层也是一个数组。并且对于 string 来说,它同时也是字符串。那么当我把 vector 的参数类型设置成 char 类型,那么从底层的角度来看,这两个容器存储的好像是同样的东西。

它们的差异点在于,虽然 vector 存储的也是字符数组,但是 vector 只存储用户显示存入的 char 类型元素,并不会在这个数组末尾自动加上 '\0' 标识。如果没有 '\0' 标识的话,vector 就不能兼容 C 语言。

并且在成员函数的设计上,string 和 vector 也不太一样,比如 string 有一个成员函数叫 append,它可以在原字符串后直接 += 另一个字符串,但是对于 vector 来说只有一个 push_back,每次只能插入一个元素。并且对于 vector 来说,它是一个类模板,也就意味着它可以存储任何类型的数据,包括 string 类型等等,比如写成:vector<string> v1; 也是没有问题的。

所以总的来说只能是 vector 的参数类型设置成 char 类型可以储存字符,变成一个字符数组,但是完全不能替代 string。

11. vector 的嵌套

对于 vector 的嵌套,我想用一道算法题目来带大家深入了解:杨辉三角。

Image

这是一道杨辉三角的算法题目,它要求我们把杨辉三角每一行的内容都打印出来。在这道算法题目里面我们需要用到二维数组,而这个二维数组我们就可以用 vector 的嵌套来实现。所以 vector<vector<int>> 实际上就是一个二维动态数组。

Image

这是力扣平台上提供的初始代码。

Image

这就是我们大概的思路:首先创建一个二维数组。每个数组里面存储的又是一个一维数组。而这个一维数组里面存储的就是杨辉三角每一行的内容。那么首先我们就要先开辟出来二维数组的大小,然后再开辟每个一维数组中的大小,先把空间设定好。

Image

这里的思路是先创建一个二维数组 vv,然后开辟 numRows 个大小类型都为 int 类型的数组,这里其实第二个参数,即 vector<int>() 也可以不写,因为 resize 的第二个参数还有缺省值,会自动调用默认构造函数然后确认类型。再调用循环将二维数组里面的每个内容都设置为 1。其中第 i 个数组就设置 i+1 个 1。

并且我们来观察一下杨辉三角,对于每一行来说,第一位和最后一位的数字都是 1,所以我们只需要对中间位置的内容进行操作。并且比如说下标为 1 的数字,其实是由上一行下标为 1 和 0 的数字相加而成的。那么假设,有一个下标为 n 的数字,那它就是由上一行的下标为 n 和 n-1 的数字相加而成的。

那么最后的代码就是这样:

Image

要注意在第二个循环里面 i 的初始值是 2,因为要从第三行开始才会有杨辉三角的特征。并且我们要保证第一行第 1 个和最后一个的数字都是 1,所以要从首位的下一位开始,到末位的前一位结束。即:size_j = 1 ; j < vv[i].size() - 1;

对于 vector 的使用就暂时讲到这里,本文到此结束。

目录

  1. 1. vector 的简要介绍
  2. 2. vector 的构造函数
  3. 3. vector 的析构函数
  4. 4. vector 的赋值运算符重载
  5. 5. vector 的遍历和访问操作
  6. 6. vector 的插入操作
  7. 7. 算法库中的 find 函数
  8. 8. vector 中的删除操作
  9. 9. vector 的关系运算符重载
  10. 10. vector 和 string 的区别
  11. 11. vector 的嵌套
  • 💰 8折买阿里云服务器限时8折了解详情
  • Magick API 一键接入全球大模型注册送1000万token查看
  • 🤖 一键搭建Deepseek满血版了解详情
  • 一键打造专属AI 智能体了解详情
极客日志微信公众号二维码

微信扫一扫,关注极客日志

微信公众号「极客日志V2」,在微信中扫描左侧二维码关注。展示文案:极客日志V2 zeeklog

更多推荐文章

查看全部
  • 2026 年前端发展趋势:AI 原生、跨端统一与全栈深化
  • Spring Boot 4.0 全面拥抱 Jackson 3 深度解析:特性、实战与迁移方案
  • MCP 技术解析:托管抓取与本地爬虫方案对比
  • C++ 类型转换详解:显式运算符与底层机制
  • Web Unlocker API 助力 AI 训练与微调数据集获取方案
  • 前端文件上传处理:优化体验与性能
  • Llama-3.2-3B 本地部署指南:Ollama + Docker 快速运行
  • HarmonyOS6 RcImage 组件核心架构与状态管理机制
  • 黑客入门指南:零基础掌握安全工程师核心能力
  • Spring Boot 集成 MyBatis 操作数据库详解
  • C++ 继承机制详解:原理、规则与实战
  • AI 编程中的 Skills:概念解析与 Java 方法生成实战
  • llama.cpp Docker 镜像国内加速下载方案
  • Stable Diffusion 提示词高阶用法实战指南
  • CISP 全类别证书详细介绍
  • 具身智能与视觉:机器人如何“看懂”世界?
  • Ollama 模型管理、删除及 Open WebUI 部署指南
  • 手势控制电脑方案分析与 Python 实战
  • 贪心算法实战:摆动序列与最长递增子序列详解
  • 40 款主流 AI 工具精选:设计、写作与协作全指南

相关免费在线工具

  • 加密/解密文本

    使用加密算法(如AES、TripleDES、Rabbit或RC4)加密和解密文本明文。 在线工具,加密/解密文本在线工具,online

  • Gemini 图片去水印

    基于开源反向 Alpha 混合算法去除 Gemini/Nano Banana 图片水印,支持批量处理与下载。 在线工具,Gemini 图片去水印在线工具,online

  • Base64 字符串编码/解码

    将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online

  • Base64 文件转换器

    将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online

  • Markdown转HTML

    将 Markdown(GFM)转为 HTML 片段,浏览器内 marked 解析;与 HTML转Markdown 互为补充。 在线工具,Markdown转HTML在线工具,online

  • HTML转Markdown

    将 HTML 片段转为 GitHub Flavored Markdown,支持标题、列表、链接、代码块与表格等;浏览器内处理,可链接预填。 在线工具,HTML转Markdown在线工具,online