
🔥承渊政道: 个人主页
❄️个人专栏: 《C语言基础语法知识》 《数据结构与算法》
✨逆境不吐心中苦,顺境不忘来时路! 🎬 博主简介:

引言:前篇文章,小编已经介绍了关于C++String类的相关知识.接下来我将带领大家继续深入学习C++的相关内容!本篇文章着重介绍关于C++Vector类以及实现Vector类的接口,那么这里面到底有哪些知识需要我们去学习的呢?废话不多说,带着这些疑问,下面跟着小编的节奏🎵一起学习吧!
目录
- 1.为什么要学习vector类?
- 2.vector类的了解
-
- [2.1 vector类的官方文档核心说明](#2.1 vector类的官方文档核心说明)
- [2.2 vector的构造函数(constructor)声明](#2.2 vector的构造函数(constructor)声明)
- [3. vector的iterator定义以及相关接口](#3. vector的iterator定义以及相关接口)
-
- [3.1 vector的iterator使用--->begin介绍](#3.1 vector的iterator使用—>begin介绍)
- [3.2 vector的iterator使用--->end介绍](#3.2 vector的iterator使用—>end介绍)
- [3.3 vector的iterator使用--->rbegin介绍](#3.3 vector的iterator使用—>rbegin介绍)
- [3.4 vector的iterator使用--->rend介绍](#3.4 vector的iterator使用—>rend介绍)
- [4. vector空间增长问题的相关接口](#4. vector空间增长问题的相关接口)
-
- [4.1 size介绍](#4.1 size介绍)
- [4.2 capacity介绍](#4.2 capacity介绍)
- [4.3 empty介绍](#4.3 empty介绍)
- [4.4 resize介绍(重点)](#4.4 resize介绍(重点))
- [4.5 reserve介绍(重点)](#4.5 reserve介绍(重点))
- [4.6 测试vector的默认扩容机制](#4.6 测试vector的默认扩容机制)
- [5. vector增删查改的相关接口](#5. vector增删查改的相关接口)
-
- [5.1 push_back介绍(重点)](#5.1 push_back介绍(重点))
- [5.2 pop_back介绍(重点)](#5.2 pop_back介绍(重点))
- [5.3 insert介绍](#5.3 insert介绍)
- [5.4 erase介绍](#5.4 erase介绍)
- [5.5 swap介绍](#5.5 swap介绍)
- [5.6 operator[]介绍(重点)](#5.6 operator[]介绍(重点))
- [6. vector迭代器失效问题(重点)](#6. vector迭代器失效问题(重点))
-
- [6.1 vector可能会导致其迭代器失效的操作有:会引起其底层空间改变的操作](#6.1 vector可能会导致其迭代器失效的操作有:会引起其底层空间改变的操作)
- [6.2 vector可能会导致其迭代器失效的操作有:指定位置元素的删除操作--erase](#6.2 vector可能会导致其迭代器失效的操作有:指定位置元素的删除操作--erase)
- [6.3 删除vector中所有的偶数请问下面哪个代码是正确的为什么?](#6.3 删除vector中所有的偶数请问下面哪个代码是正确的为什么?)
- [6.4 Linux下g++编译器对迭代器失效的检测并不是非常严格处理也没有vs下极端](#6.4 Linux下g++编译器对迭代器失效的检测并不是非常严格处理也没有vs下极端)
- [6.5 与vector类似string在插入+扩容操作+erase之后迭代器也会失效](#6.5 与vector类似string在插入+扩容操作+erase之后迭代器也会失效)
- [7. 关于vector类一些oj算法题](#7. 关于vector类一些oj算法题)
- [8. vector深度剖析](#8. vector深度剖析)
- [9. 使用memcpy拷贝问题](#9. 使用memcpy拷贝问题)
- [10. vector的核心框架接口的模拟实现](#10. vector的核心框架接口的模拟实现)
1.为什么要学习vector类?
vector是C++标准模板库(STL)中的一个序列容器,表示可以动态改变大小的数组.
vector是所有 C++ 学习者必须优先吃透的容器,没有之一 ,甚至可以说:学好vector,就掌握了C++标准库80%的核心用法,它的学习价值远超一个容器类本身.
✅ 1️⃣最核心原因:彻底解决普通数组的所有致命缺陷
你一定先学过C++的普通数组(int arr[5]/int* arr = new int[5]),数组是C++的基础,但它的缺点全是硬伤 ,而vector的设计初衷,就是完美弥补普通数组的所有短板 ,且保留数组的全部优点.
vector 对数组的完美替代:保留优点,根治缺点
✅ 保留数组的核心优势:连续内存+随机访问(下标[]) ,访问效率O(1),和数组一模一样快;
✅ 根治数组的所有痛点:长度动态可变 :想存多少元素就存多少,满了自动扩容,不用提前预估数量;
无需手动管理内存 :扩容、释放内存都是vector内部自动完成,彻底告别内存泄漏、手动拷贝的繁琐;
安全且功能丰富 :自带size()/empty()/clear()/at()(带越界检查)等方法,大幅降低出错概率。
✨ 一句话总结:vector = 增强版的超级数组,能做所有数组能做的事,还能做数组做不到的事,且更安全、更省心、更高效.
✅ 2️⃣性价比天花板:效率+易用性 做到极致平衡,没有对手
C++标准库提供了很多容器(list/deque/map/set等),但vector是唯一能做到极致效率 + 极致易用双赢 的容器,这也是它成为C++第一容器的核心原因,没有任何容器能替代它的地位 .
✔ 效率层面:vector 是综合效率最高的容器
①随机访问速度无敌 :连续内存空间,支持下标直接访问,时间复杂度O(1),比list(双向链表,无随机访问,查元素O(n))、map(红黑树,查元素O(logn))快几个量级;
②尾部操作零开销 :push_back()/pop_back()(尾部增删)效率O(1),和数组尾部操作一样快;
③内存利用率最高 :连续内存无额外开销,对比list每个元素都要存数据+前后指针,vector的内存浪费几乎可以忽略;
④缓存友好 :CPU的缓存机制对连续内存的读取效率极高,vector的元素连续存放,能被CPU缓存预加载,而list的元素分散在内存各处,缓存命中率极低.
✔ 易用层面:vector 是学习成本最低的容器
语法和普通数组高度一致 :会用数组arr[i],就会用vector[i],无需重新学习新的语法规则;
所有操作都语义清晰:push_back()(尾部加)、pop_back()(尾部删)、clear()(清空).
支持所有C++基础语法:范围for、迭代器、下标遍历,写法简洁易懂.
💡 核心结论:在C++中,无特殊需求,优先用vector是铁律 ------ 它的综合表现碾压所有其他容器,用它写代码,既快又省心.
✅ 3️⃣学习的基石价值:学好vector,等于学会C++容器的通用逻辑
vector是C++标准库最基础、最典型的容器 ,它的设计思想、核心接口、使用逻辑,是所有其他容器的通用模板 ,这是必须学vector的核心学习价值 :
✔ 触类旁通,一通百通
你学会了vector的:
初始化方式(空初始化、指定大小、列表初始化);
增删改查接口(push_back()/pop_back()/clear()/[]/at());
遍历方式(范围for、下标、迭代器);
核心属性(size()/capacity()/empty());
那么你再学list/deque/string(string本质也是字符的vector)等容器时,会发现它们的接口90%都是一样的 :比如list也有size()/empty()/clear(),也支持迭代器遍历;deque也支持下标访问、push_back().
✅ 4️⃣底层思想价值:学vector,是理解C++核心编程思想的最佳入口
✔ 思想1:理解封装的本质
C++的核心特性之一是封装 :把数据+操作数据的方法打包在一起,对外隐藏实现细节,只暴露简洁的接口.
vector就是封装的完美典范:它内部封装了底层数组、内存管理、扩容逻辑,你不用关心它是怎么扩容的、怎么释放内存的,只需要调用push_back()/pop_back()就可以操作数据;
学会vector,你就真正理解了封装的意义:让程序员专注于业务逻辑,而不是底层细节 ,这也是C++标准库的设计初衷.
✔ 思想2:理解动态内存管理的原理
C++的难点之一是动态内存管理(new/delete) ,而vector的底层就是基于动态内存实现的:vector的扩容,本质是new一块更大的内存 → 拷贝元素 →delete旧内存;vector的元素存储,本质是在堆上分配的连续内存;通过学习vector的reserve()/capacity()/扩容机制,你会彻底搞懂动态内存的工作原理 ,理解为什么会有内存泄漏、为什么数组不能动态扩容,这些知识会让你避开很多C++的经典坑.
✔ 思想3:理解模板编程的通用性
vector是一个模板类(template) ,你可以写vector<int>、vector<string>、vector<Person>,它能存储任意类型的数据.
这就是模板的核心价值:一次编写,通用所有类型 ;
学会vector,你就入门了C++的模板编程,理解了为什么C++的标准库能适配所有数据类型,这是C++泛型编程的基础.
2.vector类的了解
vector类的文档介绍
接下来关于vector类的学习我会通过上面的文档链接进行介绍和学习.
在使用vector类时,必须包含#include头文件以及using namespace std.vector.学习时一定要学会查看文档:vector类的文档介绍,vector在实际中非常的重要,在实际中我们熟悉常见的接口就可以.
2.1 vector类的官方文档核心说明

这是C++标准库中
std::vector的 官方文档核心说明
🔹开头:类模板定义template < class T, class Alloc = allocator > class vector;
vector是一个泛型模板类 ,可以存储任意类型T的元素(比如int、string、自定义类等).
Alloc是内存分配器(默认用标准的allocator<T>),负责底层内存的申请与释放,你几乎不需要手动修改它。必须包含头文件
<vector>才能使用.
🔹核心定位:动态可变长数组Vectors are sequence containers representing arrays that can change in size.
本质 :vector是动态大小的顺序容器,可以理解为自动扩容的数组 .和普通数组的共同点:元素在内存中连续存储 ,因此支持通过指针偏移、下标
[]随机访问,效率和普通数组完全一致(时间复杂度O(1)).和普通数组的不同点:大小可以动态变化 ,内存由容器自动管理,不需要你手动调用
new/delete分配或释放内存.
🔹内部实现与扩容机制这部分是理解
vector性能的关键:①底层存储 :用动态分配的数组保存元素.当新增元素导致数组存满时,需要重新分配内存 :
步骤:申请一块更大的新内存 → 把旧数组的元素全部移动到新内存 → 释放旧内存.
代价:这个过程很耗时(时间复杂度
O(n)),因此vector不会每次添加元素都触发扩容.②预分配与容量(capacity) :
为了减少扩容次数,
vector会预分配额外的内存空间 ,因此容器的容量(capacity()),会大于实际元素数(size())扩容策略:不同编译器实现不同(通常是原容量的2倍或1.5倍),但核心是让扩容频率足够低,保证尾部插入元素(
push_back)的均摊时间复杂度为O(1)(大部分时候是O(1),偶尔扩容是O(n),平均下来还是O(1)).③内存代价 :
因为预分配了额外空间,
vector比普通数组占用更多内存,但换来了动态扩容 + 自动内存管理的高效性.
🔹和其他动态容器的对比Compared to the other dynamic sequence containers (deques, lists and forward_lists)
| 特性 | vector 表现 |
其他容器(如 list)表现 |
|---|---|---|
| 随机访问 | 效率极高(O(1),和数组一致) |
无随机访问(list 需遍历,O(n)) |
| 尾部增删元素 | 效率高(O(1)) |
效率高(list 也是 O(1)) |
| 非尾部增删元素 | 效率极低(O(n),需移动元素) |
效率高(list 只需修改指针,O(1)) |
| 迭代器稳定性 | 扩容后旧迭代器会失效 | list 的迭代器始终有效 |
2.2 vector的构造函数(constructor)声明

这是C++标准库中
std::vector的构造函数(constructor) 官方文档说明,详细列出了创建vector对象的 4 种核心方式.
📌 整体说明
构造函数的作用是 初始化vector对象**,不同版本的构造函数对应不同的初始化场景.文档里的 4 种构造函数覆盖了空容器、固定大小填充、范围拷贝、容器复制四大常用场景.
🔹Empty container constructor(默认构造函数)explicit vector (const allocator_type& alloc = allocator_type());
作用 :创建一个空的vector,不含任何元素,size()和capacity()初始为0(或极小预分配,依实现而定).
示例 :vector v; --->空容器,后续可动态添加元素
适用场景 :提前不确定元素数量,需要后续动态添加数据(如读取用户输入、从文件加载数据).
🔹Fill constructor(填充构造函数)explicit vector (size_type n, const value_type& val = value_type(),
const allocator_type& alloc = allocator_type());
作用 :创建包含n个元素 的vector,每个元素都是val的拷贝;若不指定val,则用元素类型的默认值(如int默认是0,string默认是空串).
示例 :vector v1(5); // 5个元素,每个都是0
vector v2(5, 10); // 5个元素,每个都是10
适用场景 :提前知道元素数量,且所有元素初始值相同(如初始化固定大小的数组、统一赋值场景).
🔹Range constructor(范围构造函数)template
vector (InputIterator first, InputIterator last,
const allocator_type& alloc = allocator_type());
作用 :用一个**迭代器范围[first, last)**内的元素初始化vector,将该范围内的元素按顺序拷贝到新容器中.
示例 :// 从另一个vector拷贝
vector v1{1,2,3};
vector v2(v1.begin(), v1.end()); // v2和v1完全相同
// 从普通数组拷贝
int arr[] = {4,5,6};
vector v3(arr, arr+3); // v3包含4,5,6
适用场景 :需要从另一个容器(如数组、list、其他vector)中拷贝一段元素来初始化新容器.
🔹Copy constructor(拷贝构造函数)vector (const vector& x);
作用 :拷贝另一个同类型的vector x,创建一个与x完全相同的新vector,包含x的所有元素且顺序一致.
示例 :vector v1{1,2,3};
vector v2(v1); // v2是v1的独立副本,修改v2不影响v1
适用场景 :需要复制已有vector,得到一个独立的副本(避免原容器被意外修改).💡补充:关于内存分配器(allocator)
每个构造函数都有一个可选的
alloc参数,用于指定内存分配器,默认使用标准的allocator<T>.绝大多数开发场景下不需要手动指定,除非你有特殊的内存管理需求(如自定义内存池).
📋 构造函数选择指南
| 需求场景 | 推荐使用的构造函数 |
|---|---|
| 创建空容器,后续动态添加 | 默认构造函数(1) |
| 固定数量+统一初始值 | 填充构造函数(2) |
| 从其他容器/数组拷贝一段 | 范围构造函数(3) |
| 复制已有 vector | 拷贝构造函数(4) |
3. vector的iterator定义以及相关接口
vector的迭代器(iterator)是 STL 中最典型的随机访问迭代器 ,它的设计和用法都和原生指针高度相似,因为vector底层是连续内存存储,这让它的迭代器拥有了极高的效率和灵活性.
✨ 1️⃣本质与定位
本质 :vector的迭代器是一个随机访问迭代器(Random Access Iterator) ,在多数编译器实现中,它就是一个原生指针(比如vector<int>::iterator等价于int*).
核心作用 :作为容器和算法的桥梁,用于遍历、访问、修改vector中的元素,同时兼容 STL 标准算法(如sort、find、for_each).
核心特性 :支持随机访问(通过+/-直接跳转到任意位置),时间复杂度为O(1),这是它区别于list等容器迭代器的关键.
📊 2️⃣vector 迭代器的核心类型
vector提供了 4 种迭代器类型,满足不同场景的访问需求:
| 迭代器类型 | 访问权限 | 遍历方向 | 适用场景 |
|---|---|---|---|
vector<T>::iterator |
可读可写 | 正向遍历 | 需要修改元素的遍历 |
vector<T>::const_iterator |
只读不可写 | 正向遍历 | 仅读取元素,保证数据安全 |
vector<T>::reverse_iterator |
可读可写 | 反向遍历 | 从尾部向头部遍历并修改元素 |
vector<T>::const_reverse_iterator |
只读不可写 | 反向遍历 | 仅反向读取元素 |
3.1 vector的iterator使用--->begin介绍
这是C++ 标准库中std::vector::begin()成员函数的官方文档说明.📌 核心功能
begin()的作用是返回一个指向vector第一个元素的迭代器 ,是遍历容器的起点.
🔹函数签名(两个重载版本)iterator begin(); // 非 const 版本
const_iterator begin() const; //const 版本(C++98 起支持)
非const 版本 :当vector是可修改的(非 const 对象)时调用,返回iterator(可读可写迭代器),可以通过迭代器修改元素.
const 版本 :当vector是只读的(const 对象)时调用,返回const_iterator(只读迭代器),只能读取元素,不能修改.
🔹关键区别:begin()vsfront()文档里特意强调了这一点:
vector::front()→ 返回第一个元素的引用 (直接拿到元素值,比如int val = v.front();).
vector::begin()→ 返回指向第一个元素的随机访问迭代器 (用来定位/遍历元素,比如auto it = v.begin(); int val = *it;).
⚠️ 重要注意事项如果
vector是空的(v.empty() == true),调用begin()返回的迭代器不能被解引用 (因为没有元素可以指向,解引用会导致未定义行为,比如程序崩溃).📋 参数与返回值
参数 :无参数,直接调用即可.
返回值 :若
vector是const对象,返回const_iterator(只读).若
vector是非const对象,返回iterator(可读可写).两种迭代器都属于随机访问迭代器 ,支持
+/-偏移、</>比较等操作.
3.2 vector的iterator使用--->end介绍
这是C++ 标准库中std::vector::end()成员函数的官方文档说明.📌 核心功能
end()的作用是返回一个指向vector尾后位置(past-the-end)的迭代器 ,这个位置是理论上在最后一个元素之后的虚拟位置,不指向任何实际元素.
🔹函数签名(两个重载版本)iterator end(); // 非 const 版本
const_iterator end() const; // const 版本(C++98 起支持)
非 const 版本 :当vector是可修改的(非 const 对象)时调用,返回iterator(可读可写迭代器).
const 版本 :当vector是只读的(const 对象)时调用,返回const_iterator(只读迭代器).
🔹关键概念:尾后位置(past-the-end)这是理解
end()的核心:它不是指向最后一个元素,而是指向最后一个元素的下一个位置 (虚拟位置,无实际元素).因此,
end()返回的迭代器绝对不能被解引用 (*it会导致未定义行为,比如程序崩溃).STL 中所有容器的范围都是左闭右开区间
[begin(), end()),这个设计保证了:遍历条件
it != v.end()可以覆盖所有元素.空容器时
begin() == end(),便于判断容器是否为空.
🔹与back()的区别(重要对比)
| 函数 | 返回值类型 | 含义 |
|---|---|---|
v.back() |
元素的引用 | 直接获取最后一个元素的值 |
v.end() |
迭代器 | 指向尾后虚拟位置,不能解引用 |
⚠️ 重要注意事项
①空容器的特殊情况 :如果
vector是空的(v.empty() == true),end()和begin()返回的是同一个迭代器,即v.begin() == v.end().②迭代器不能解引用 :
end()指向的是虚拟位置,没有实际元素,解引用会导致程序崩溃或未定义行为.③配合
begin()遍历 :begin()和end()是遍历容器的标准组合,比如for (auto it = v.begin(); it != v.end(); ++it).📋 参数与返回值
参数 :无参数,直接调用即可.
返回值 :若
vector是const对象,返回const_iterator(只读).若
vector是非const对象,返回iterator(可读可写).两种迭代器都属于随机访问迭代器 ,支持
+/-偏移、</>比较等操作.
3.3 vector的iterator使用--->rbegin介绍

这是C++标准库中
std::vector::rbegin()成员函数的官方文档说明.📌 核心功能
rbegin()的作用是返回一个反向迭代器,指向vector的最后一个元素 (也就是反向遍历的起点).反向迭代器的特点是:递增(
++it)时会向容器的开头移动 ,递减(--it)时会向容器的末尾移动.
🔹函数签名(两个重载版本)reverse_iterator rbegin(); // 非 const 版本
const_reverse_iterator rbegin() const; // const 版本(C++98 起支持)
非 const 版本 :当vector是可修改的(非 const 对象)时调用,返回reverse_iterator(可读可写的反向迭代器).
const 版本 :当vector是只读的(const 对象)时调用,返回const_reverse_iterator(只读的反向迭代器).
🔹关键概念与对比①和
end()的关系
rbegin()指向的元素,正好是end()指向的尾后虚拟位置的前一个元素(也就是vector的最后一个实际元素).简单说:
v.end()→ 指向尾后虚拟位置
v.rbegin()→ 指向最后一个实际元素(v.end()的前一个位置)②和
back()的区别
| 函数 | 返回值类型 | 含义 |
|---|---|---|
v.back() |
元素的引用 | 直接获取最后一个元素的值 |
v.rbegin() |
反向迭代器 | 指向最后一个元素,用于反向遍历 |
⚠️ 重要注意事项
①反向迭代器的遍历逻辑 :对反向迭代器执行
++it,会让它向容器的开头 移动(比如从最后一个元素到倒数第二个元素);执行--it会向容器的末尾 移动.
② 空容器的情况**:如果vector是空的(v.empty() == true),rbegin()和rend()返回的是同一个迭代器,不能解引用.③迭代器的可解引用性 :
rbegin()指向的是实际元素(最后一个元素),因此可以安全解引用;但如果容器为空,则不能解引用.📋 参数与返回值
参数 :无参数,直接调用即可.
返回值 :若
vector是const对象,返回const_reverse_iterator(只读).若
vector是非const对象,返回reverse_iterator(可读可写).两种迭代器都属于反向随机访问迭代器 ,支持
+/-偏移、</>比较等操作.
3.4 vector的iterator使用--->rend介绍

这是C++标准库中
std::vector::rend()成员函数的官方文档说明📌 核心功能
rend()的作用是返回一个反向迭代器,指向vector第一个元素之前的虚拟位置 (也就是反向遍历的终点).它和
rbegin()配合,构成反向遍历的左闭右开区间[rbegin(), rend()),覆盖容器的所有元素(逆序)
🔹函数签名(两个重载版本)reverse_iterator rend(); // 非 const 版本
const_reverse_iterator rend() const; // const 版本(C++98 起支持)
非 const 版本 :当vector是可修改的(非 const 对象)时调用,返回reverse_iterator(可读可写的反向迭代器).
const 版本 :当vector是只读的(const 对象)时调用,返回const_reverse_iterator(只读的反向迭代器).
🔹关键概念与对比①反向遍历的范围
反向迭代器的遍历逻辑是:
rbegin()→ 指向最后一个元素(反向遍历的起点)
rend()→ 指向第一个元素之前的虚拟位置(反向遍历的终点)区间
[rbegin(), rend())包含容器的所有元素,按逆序 排列(从最后一个到第一个).②和
begin()的关系
rend()指向的虚拟位置,正好是begin()指向的第一个元素的前一个位置.简单说:
v.begin()→ 指向第一个实际元素
v.rend()→ 指向第一个元素之前的虚拟位置③不能解引用
rend()指向的是虚拟位置,没有实际元素,因此绝对不能解引用 (*it会导致未定义行为,比如程序崩溃).
⚠️ 重要注意事项
① 空容器的情况**:如果vector是空的(v.empty() == true),rbegin()和rend()返回的是同一个迭代器,即v.rbegin() == v.rend().②反向迭代器的移动逻辑 :对反向迭代器执行
++it,会向容器的开头 移动;执行--it,会向容器的末尾 移动.③遍历终止条件 :反向遍历时,循环条件是
it != v.rend()(而不是it < v.rend()),这是 STL 容器遍历的统一规范.📋 参数与返回值
参数 :无参数,直接调用即可.
返回值 :若
vector是const对象,返回const_reverse_iterator(只读).若
vector是非const对象,返回reverse_iterator(可读可写).两种迭代器都属于反向随机访问迭代器 ,支持
+/-偏移、</>比较等操作.
4. vector空间增长问题的相关接口
4.1 size介绍
这是C++标准库中std::vector::size()成员函数的官方文档说明📌 核心功能
size()的作用是返回vector中当前实际存储的元素个数 ,是判断容器元素数量、控制遍历范围的核心函数.
🔹函数签名size_type size() const;
const成员函数 :调用时不会修改vector的内容,是只读操作.
返回值类型size_type:这是一个无符号整数类型 (通常等价于size_t),因此返回值永远是非负的.
🔹关键概念:size()vscapacity()文档特别强调了两者的区别,这是理解
vector内存管理的核心:
| 函数 | 含义 | 示例场景 |
|---|---|---|
v.size() |
实际元素个数:当前容器中已存储的元素数量 | 遍历容器时用 for (int i=0; i<v.size(); i++) |
v.capacity() |
容量:底层分配的内存空间最多能容纳的元素数量 | 预分配内存时用 v.reserve(n) 提升性能 |
举个例子:
如果
vector预分配了10个元素的内存(capacity()=10),但只存了5个元素,那么size()=5.
⚠️ 重要注意事项①无符号类型的坑 :
因为
size_type是无符号整数,所以不要用它和负数比较,也不要在减法中让结果为负.例如:
if (v.size() - 1 < 0) { ... } // 错误!无符号数减1会溢出成极大的正数
正确写法是先判断
v.size() > 0,再做减法.②空容器的情况 :
当
vector为空时,size()返回0,可以配合empty()一起判断容器状态(empty()比size() == 0更高效).📋 参数与返回值
参数 :无参数,直接调用即可.
返回值 :容器中实际元素的个数,类型为无符号整数size_type.
4.2 capacity介绍

这是C++ 标准库中
std::vector::capacity()成员函数的官方文档说明📌 核心功能
capacity()的作用是返回vector当前分配的存储空间能容纳的元素数量 (即容量),它反映了容器在不触发扩容的情况下最多能存多少元素.
🔹函数签名size_type capacity() const;
const成员函数 :调用时不会修改vector的内容,是只读操作.
返回值类型size_type:无符号整数类型(通常等价于size_t),返回值永远是非负的.
🔹关键概念对比:capacity()vssize()vsmax_size()文档里重点区分了这三个核心属性,这是理解
vector内存管理的关键:
| 函数 | 含义 |
|---|---|
v.size() |
实际元素数:当前容器中已存储的元素数量。 |
v.capacity() |
容量:当前分配的内存空间最多能容纳的元素数量(不扩容的情况下)。 |
v.max_size() |
理论最大容量:容器在系统内存限制下,理论上能容纳的最大元素数。 |
核心逻辑:
capacity() >= size(),因为vector会预分配额外空间以减少扩容次数;当size() == capacity()时,再添加元素就会触发自动扩容.🔹 扩容机制与优化
①自动扩容触发条件 :当
push_back()/insert()等操作导致size() == capacity()时,vector会自动扩容.扩容过程:申请一块更大的新内存(通常是原容量的 2 倍或 1.5 倍)→ 拷贝旧元素到新内存 → 释放旧内存.
代价:扩容的时间复杂度是
O(n),频繁扩容会影响性能.②手动优化容量 :可以用
v.reserve(n)提前预留容量,让capacity()直接达到n,避免后续频繁扩容.例如:如果提前知道要存 1000 个元素,调用
v.reserve(1000)可以让容器一次性分配足够内存,大幅提升性能.
⚠️ 重要注意事项①不会自动缩容 :
vector的容量只会在扩容时增加,不会因为删除元素而自动减少(即使size()变小,capacity()也保持不变).若需要释放多余内存,可使用
v.shrink_to_fit()(C++11 起支持),但这是请求式操作,编译器不一定会执行.②无符号类型的坑 :
size_type是无符号整数,避免用它和负数比较,也不要在减法中让结果为负.③容量不是上限 :
capacity()只是当前分配的内存容量,当需要更多空间时,容器会自动扩容,理论上限由max_size()决定.
4.3 empty介绍

这是C++标准库中
std::vector::empty()成员函数的官方文档说明.📌 核心功能
empty()的作用是判断vector是否为空 (即容器中是否没有任何元素),是最常用的容器状态检查函数之一.
🔹函数签名bool empty() const;
const成员函数 :调用时不会修改vector的内容,是纯查询操作.
返回值类型 :bool,容器为空时返回true,非空时返回false.
🔹关键细节①等价于
size() == 0,但更高效文档明确说明:
empty()的本质是检查size()是否为0.对于
vector来说,size()是直接存储的成员变量,所以empty()和size() == 0效率几乎相同;但对于其他容器(如list),size()可能需要遍历计算,此时empty()是更高效的判断方式.因此,判断容器是否为空时,优先使用
empty()而非size() == 0,这是 STL 的最佳实践.②不修改容器
empty()仅查询状态,不会改变容器的元素或内存.如果需要清空容器的内容,请使用vector::clear().
⚠️ 重要注意事项
空容器的迭代器风险 :如果empty()返回true,则begin()和end()返回的迭代器是同一个,不能解引用(否则会导致未定义行为).
返回值是布尔类型:直接用于条件判断即可,无需额外转换.
4.4 resize介绍(重点)

这是C++标准库中
std::vector::resize()成员函数的官方文档说明📌 核心功能
resize()的作用是主动修改vector的元素数量(size) ,让容器最终包含n个元素.它会根据n和当前size的关系,自动执行截断元素或添加元素的操作.
🔹函数签名void resize (size_type n, value_type val = value_type());
参数n:目标元素数量(无符号整数类型size_type).
参数val(可选) :当需要新增元素时,用val作为新增元素的初始值;如果不指定,新增元素会用默认值初始化 (如int默认是0,string默认是空串).
无返回值 :直接修改容器本身.
🔹三种行为逻辑(核心重点)根据目标数量
n和当前容器size()的关系,resize()会执行不同操作:①✂️ 当
n < 当前 size():截断元素容器会保留前
n个元素 ,删除从第n+1个到末尾的所有元素(并销毁这些元素).此时size()变为n,但capacity()保持不变(因为只是删除元素,不需要释放内存).②➕ 当
当前 size() < n ≤ 当前 capacity():尾部添加元素容器会在尾部添加
n - size()个新元素,用val(或默认值)初始化.此时size()变为n,capacity()仍保持不变(因为预分配的内存足够容纳新增元素).③🚀当
n > 当前 capacity():扩容 + 添加元素容器会先自动扩容 (分配新的更大内存,通常是原容量的 2 倍),然后在尾部添加
n - size()个新元素.此时
size()和capacity()都会更新(size()变为n,capacity()变为扩容后的容量).
🔹关键对比:resize()vsreserve()这是最容易混淆的两个函数,必须明确区分:
| 函数 | 作用 | 是否修改元素数量 | 是否影响容量 |
|---|---|---|---|
v.resize(n) |
改变元素数量,让 size() 等于 n |
✅ 是(添加/删除元素) | ❌ 仅当 n > capacity() 时扩容 |
v.reserve(n) |
预分配内存,让 capacity() 至少为 n |
❌ 不修改元素数量 | ✅ 是(仅预分配内存) |
举个例子:
v.resize(10)→ 让容器有10个元素(不够就加,多了就删).
v.reserve(10)→ 让容器能容纳10个元素(但当前元素数量不变).
⚠️重要注意事项①默认初始化的坑 :如果不指定
val,新增元素会用value_type()初始化.对于内置类型(如int)是0,但对于自定义类型,需要确保该类型有默认构造函数.②元素销毁风险 :当
n < size()时,被截断的元素会被销毁,若元素是指针或包含动态内存,需注意内存泄漏问题.③无符号参数的坑 :
n是无符号整数,不能传入负数,否则会触发溢出(变成极大的正数,导致意外扩容/崩溃).
4.5 reserve介绍(重点)

这是C++标准库中
std::vector::reserve()成员函数的官方文档说明.📌 核心功能
reserve()的作用是预分配内存 ,让vector的容量(capacity())至少能容纳n个元素.它仅改变容器的内存分配,不会修改元素数量(size()) ,也不会添加或删除任何元素.
🔹函数签名void reserve (size_type n);
参数n:要求的最小容量(无符号整数类型size_type).
无返回值 :直接修改容器的内存分配状态.
🔹两种行为逻辑根据
n和当前capacity()的关系,reserve()会执行不同操作:①🚀 当
n > 当前 capacity():触发扩容容器会重新分配一块更大的内存,容量至少为
n(通常编译器会分配比n更大的空间,比如原容量的 2 倍,以减少后续扩容次数).扩容后,原元素会被移动到新内存,旧内存被释放.此时
capacity()会更新为 ≥n,但size()保持不变.②🛑 当
n ≤ 当前 capacity():无操作容器不会分配新内存,
capacity()保持不变.这也意味着
reserve()不会主动缩容 (如果需要释放多余内存,需用shrink_to_fit(),C++11 起支持).
🔹关键对比:reserve()vsresize()这是最容易混淆的两个函数,必须明确区分:
| 函数 | 作用 | 是否修改元素数量 | 是否影响容量 |
|---|---|---|---|
v.reserve(n) |
预分配内存,让 capacity() ≥ n |
❌ 不修改元素数量 | ✅ 仅当 n > capacity() 时扩容 |
v.resize(n) |
改变元素数量,让 size() = n |
✅ 是(添加/删除元素) | ❌ 仅当 n > capacity() 时扩容 |
举个例子:
v.reserve(100)→ 让容器能容纳100个元素(但当前元素数量不变).
v.resize(100)→ 让容器有100个元素(不够就加默认值,多了就删).
⚠️重要注意事项①不影响元素数量 :
reserve()不会添加或删除元素,size()始终不变。②不会缩容 :如果
n小于当前capacity(),容器不会释放多余内存,避免频繁分配/释放的开销.③异常处理 :
若
n超过容器的理论最大容量(max_size()),会抛出length_error异常.若内存分配失败(比如系统内存不足),会抛出
bad_alloc异常.④无符号参数的坑 :
n是无符号整数,不能传入负数,否则会溢出成极大的正数,导致意外扩容.
💡 使用场景
reserve()主要用于性能优化 :当你提前知道要存储的元素数量时,用
reserve(n)一次性分配足够内存,避免后续频繁扩容(扩容的时间复杂度是O(n),会影响性能).例如:读取一个包含 10000 行的文件时,先调用
v.reserve(10000),再逐行添加元素,可大幅提升效率.
4.6 测试vector的默认扩容机制
capacity的代码在vs和g++下分别运行会发现,vs下capacity是按1.5倍增长的,g++是按2倍增长的.这个问题经常会考察,不要固化的认为,vector增容都是2倍,具体增长多少是根据具体的需求定义的.vs是PJ版本STL,g++是SGI版本STL.reserve只负责开辟空间,如果确定知道需要用多少空间,reserve可以缓解vector增容的代价缺陷问题.resize在开空间的同时还会进行初始化,影响size.
cpp
#include <iostream>
#include <vector>
using namespace std;
void TestVectorExpand()
{
size_t sz;
vector<int> v;
sz = v.capacity();
cout << "making v grow:\n";
for (int i = 0; i < 100; ++i)
{
v.push_back(i);
if (sz != v.capacity())
{
sz = v.capacity();
cout << "capacity changed: " << sz << '\n';
}
}
}
int main()
{
TestVectorExpand();
return 0;
}
cpp
//如果已经确定vector中要存储元素大概个数,可以提前将空间设置足够
//就可以避免边插入边扩容导致效率低下的问题了
#include <iostream>
#include <vector>
using namespace std;
void TestVectorExpandOP()
{
vector<int> v;
size_t sz = v.capacity();
v.reserve(100); // 提前将容量设置好,可以避免一遍插入一遍扩容
cout << "making bar grow:\n";
for (int i = 0; i < 100; ++i)
{
v.push_back(i);
if (sz != v.capacity())
{
sz = v.capacity();
cout << "capacity changed: " << sz << '\n';
}
}
}
int main()
{
TestVectorExpandOP();
return 0;
}
5. vector增删查改的相关接口
5.1 push_back介绍(重点)

这是C++ 标准库中
std::vector::push_back()成员函数的官方文档说明📌 核心功能
push_back()是vector最常用的添加元素的方法,作用是在容器的尾部追加一个新元素 ,直接把元素val拷贝(或移动)到容器的末尾.
🔹函数签名void push_back (const value_type& val);
参数val:要添加的元素的常量引用,会被拷贝(或移动)到容器的新元素中.
无返回值 :直接修改容器本身。
🔹行为逻辑①元素追加 :新元素会被放在当前最后一个元素的后面,容器的
size()会增加 1 .②自动扩容触发条件 :如果添加元素后,新的
size()超过了当前capacity(),容器会触发自动扩容.扩容过程:申请一块更大的新内存(通常是原容量的 2 倍或 1.5 倍)→ 将旧元素移动到新内存 → 释放旧内存.扩容后,原有的迭代器、指针和引用都会失效(因为底层内存地址变了).
🔹关键细节①时间复杂度:均摊 O(1)大部分情况下,尾部追加元素的时间复杂度是
O(1)(直接在预留内存中添加).只有当触发扩容时,时间复杂度是
O(n)(需要移动所有元素).因为扩容频率很低(容量指数增长),所以均摊时间复杂度是
O(1),这也是push_back高效的原因.②和
emplace_back()的区别(高频考点)
| 函数 | 实现方式 | 效率 |
|---|---|---|
push_back(val) |
先构造元素,再拷贝/移动到容器 | 有拷贝/移动开销 |
emplace_back(...) |
直接在容器尾部构造元素 | 无拷贝/移动开销,效率更高(C++11+推荐) |
⚠️ 重要注意事项
①迭代器失效风险 :扩容后,所有指向原容器的迭代器、指针和引用都会失效,需要重新获取.
②元素拷贝/移动 :如果元素是自定义类型,需要确保该类型支持拷贝构造(或移动构造),否则无法使用
push_back.③异常处理 :如果扩容时内存分配失败(如系统内存不足),会抛出
bad_alloc异常;如果元素的拷贝/移动构造函数抛出异常,容器会回滚到操作前的状态.④容量预优化 :如果提前知道要添加大量元素,建议先用
reserve(n)预分配容量,避免频繁扩容,提升性能.
5.2 pop_back介绍(重点)
这是C++标准库中std::vector::pop_back()成员函数的官方文档说明.📌 核心功能
pop_back()是vector中用于删除尾部元素 的核心操作,直接让容器的元素数量(size())减少 1,并销毁被删除的元素.
🔹函数签名void pop_back();
无参数 :无需传入任何值,直接对容器尾部进行操作.
无返回值 :仅修改容器本身,不会返回被删除的元素(若需要获取最后一个元素再删除,可先调用back()再执行pop_back()).
🔹 行为逻辑①元素删除 :仅删除最后一个元素,
size()减1,但容器的容量(capacity())保持不变(vector不会因删除元素自动缩容).②元素销毁 :被删除的元素会调用其析构函数完成销毁,若元素包含动态内存(如指针、自定义对象),需确保内存已正确释放以避免泄漏.
③时间复杂度 :
O(1),因为仅操作尾部,无需移动其他元素,效率极高.
⚠️ 重要注意事项①空容器禁止调用 :如果
vector为空(empty() == true),调用pop_back()会触发未定义行为 (如程序崩溃).正确做法:调用前必须用
empty()判断容器是否非空.②迭代器失效范围 :
仅指向被删除的尾部元素 的迭代器/引用会失效.
其他位置的迭代器和引用不受影响(因为没有元素移动).
③不会主动缩容 :即使删除元素后
size()远小于capacity(),容器也不会释放多余内存.若需要释放内存,可使用shrink_to_fit()(C++11 起支持).
💡 典型使用场景
pop_back()是实现**栈(LIFO,后进先出)**结构的核心操作,因为尾部删除的效率为O(1),与栈"最后入栈、最先出栈"的特性完全匹配.
5.3 insert介绍
这是C++标准库中std::vector::insert()成员函数的官方文档说明.📌 核心功能
insert()的作用是在vector的指定迭代器位置position之前 插入新元素,从而扩展容器的元素数量(size()增加插入的元素个数).插入后,原位置及之后的元素会向后移动,可能触发自动扩容.
🔹主要重载版本(4种常用场景)文档中列出了 4 种核心重载形式,覆盖不同插入需求:
| 重载版本 | 签名 | 作用 |
|---|---|---|
| 1. 单个元素插入 | iterator insert (iterator position, const value_type& val); |
在 position 前插入**1个值为 val**的元素,返回指向新插入元素的迭代器。 |
| 2. 填充插入 | void insert (iterator position, size_type n, const value_type& val); |
在 position 前插入**n个值为 val**的元素。 |
| 3. 范围插入 | template <class InputIterator> void insert (iterator position, InputIterator first, InputIterator last); |
在 position 前插入**另一个容器/迭代器范围 [first, last)**的所有元素(按原顺序拷贝)。 |
| 4. 初始化列表插入(C++11+) | void insert (iterator position, initializer_list<value_type> il); |
在 position 前插入**初始化列表 il**中的所有元素。 |
🔹行为逻辑与性能特点
①扩容触发条件 :当插入后新的
size()超过当前capacity()时,容器会自动扩容(分配更大的内存、移动旧元素、释放旧内存).②元素移动开销 :由于
vector是连续内存存储,若插入位置不是尾部(end()) ,该位置之后的所有元素都需要向后移动,时间复杂度为O(n)(n为移动的元素个数).因此,非尾部插入效率较低 ,频繁在中间插入会严重影响性能.③效率对比 :与
list等链表容器相比,vector::insert效率更低(链表插入仅需修改指针,无需移动元素).若需频繁在中间插入,建议优先使用list或deque.
🔹参数详解
| 参数 | 含义 |
|---|---|
position |
迭代器,指定插入的位置(新元素会放在该迭代器指向的元素之前)。 |
val |
插入元素的值,会被拷贝(或移动)到新元素中。 |
n |
填充插入时,要插入的元素个数(无符号整数类型)。 |
first, last |
迭代器范围 [first, last),表示要插入的元素来自这个范围(包含 first,不包含 last)。 |
il |
初始化列表(C++11+),插入列表中的所有元素。 |
🔹返回值
①单个元素插入版本(1) :返回指向第一个新插入元素 的迭代器(可用于更新后续操作的迭代器,避免失效).
②其他版本(2/3/4) :无返回值(仅修改容器本身).
⚠️ 重要注意事项①迭代器失效风险 :
扩容后,所有原迭代器、指针、引用都会失效 (底层内存地址改变).即使不扩容,插入位置之后的迭代器、指针、引用也会失效 (元素移动导致位置变化).
解决方法:插入后通过返回值(或重新调用
begin()/end())获取新的迭代器.②空容器插入 :若
vector为空,position可以是begin()(或end(),空容器时两者相等),插入后元素成为第一个元素.③异常安全 :扩容时内存分配失败会抛出
bad_alloc异常;若元素的拷贝/移动构造函数抛出异常,容器会回滚到插入前的状态.
5.4 erase介绍
这是C++标准库中std::vector::erase()成员函数的官方文档说明.📌 核心功能
erase()的作用是删除vector中的单个元素或一个范围的元素 ,直接减少容器的元素数量(size()),并销毁被删除的元素.
🔹函数签名(两个重载版本)// 1. 删除单个元素
iterator erase (iterator position);
// 2. 删除一个范围的元素
iterator erase (iterator first, iterator last);
两个版本都返回迭代器 :指向被删除元素的下一个位置(若删除的是最后一个元素,则返回end()).
🔹行为逻辑与性能特点①元素移动开销 :
因为
vector是连续内存存储,若删除位置不是尾部(end()) ,该位置之后的所有元素都会向前移动,时间复杂度为O(n)(n为移动的元素个数).因此,非尾部删除效率较低 ,频繁在中间删除会严重影响性能.②效率对比 :与
list等链表容器相比,vector::erase效率更低(链表删除仅需修改指针,无需移动元素).若需频繁在中间删除,建议优先使用list或deque.③元素销毁 :被删除的元素会调用其析构函数完成销毁,若元素包含动态内存(如指针、自定义对象),需确保内存已正确释放以避免泄漏.
🔹参数详解
| 参数 | 含义 |
|---|---|
position |
迭代器,指向要删除的单个元素。 |
first, last |
迭代器范围 [first, last),表示要删除的元素范围(包含 first,不包含 last)。 |
🔹返回值
返回一个迭代器,指向被删除元素的下一个位置 :
若删除单个元素
position,返回指向position下一个元素的迭代器.若删除范围
[first, last),返回指向last位置的迭代器.若删除的是最后一个元素,返回
end().这个返回值非常重要,因为删除后原迭代器会失效,必须用它来更新后续操作的迭代器,避免未定义行为.
⚠️ 重要注意事项①迭代器失效风险 :
删除位置之后的所有迭代器、指针、引用都会失效(因为元素向前移动)。
解决方法:用
erase()的返回值更新迭代器(例如it = v.erase(it)),而不是直接it++。②循环删除的正确写法 :
错误写法(迭代器失效,导致崩溃):
for (auto it = v.begin(); it != v.end();++it) {
if (*it == 20) v.erase(it); //erase后it失效,++it会导致未定义行为
}
正确写法(用返回值更新迭代器):
for (auto it = v.begin(); it ! = v.end())
{
if (*it == 20) {
it = v.erase(it); // 用返回值更新it,避免失效
} else {
++it;
}
}
③空容器或无效迭代器 :若
vector为空,或迭代器超出范围,调用erase()会触发未定义行为(如程序崩溃)。④容量不变 :删除元素后,
capacity()保持不变(vector不会因删除元素自动缩容,若需释放内存可使用shrink_to_fit())。
5.5 swap介绍
这是C++标准库中std::vector::swap()成员函数的官方文档说明.📌 核心功能
swap()的作用是交换两个同类型vector的全部内容 ,是一个高效的操作,时间复杂度为O(1)(仅交换内部的指针和元数据,不移动元素).
🔹函数签名void swap (vector& x);
参数x:另一个同类型的vector(即模板参数T和Alloc完全相同),通过引用传递.
无返回值 :直接修改两个容器的内部状态.
🔹行为逻辑①内容交换 :交换后,当前容器的元素变为
x原来的元素,x的元素变为当前容器原来的元素.②迭代器/引用有效性 :交换完成后,指向原容器元素的迭代器、指针、引用仍然有效 ,只是现在指向另一个容器的对应元素(因为元素本身的内存地址没有变化,只是容器的管理结构被交换了).
③效率优势 :不同于元素拷贝,
swap()仅交换容器内部的数据指针、容量、大小等元数据,因此时间复杂度为O(1),是性能极高的操作.
🔹关键细节①成员函数 vs 非成员函数
std::swap
成员函数v.swap(x):是vector特有的优化版本,保证O(1)时间复杂度.
非成员函数std::swap(v, x):在 C++11 及以后,会自动调用成员函数版本,因此效率一致;但在旧标准中可能是元素拷贝(O(n)),因此推荐优先使用成员函数.②分配器兼容性
文档中提到"no specifics on allocators",但通常:若两个
vector的分配器兼容(默认分配器std::allocator总是兼容的),则交换是纯元数据交换(O(1)).若分配器不兼容,可能会触发元素拷贝(但同类型vector的分配器通常是兼容的).③vector
的特殊情况 vector<bool>是vector的特化版本(位压缩存储),它为swap()提供了额外的重载,以优化位数据的交换效率.
⚠️ 重要注意事项①类型必须完全匹配 :交换的两个
vector必须是同类型 (元素类型和分配器都相同),否则编译错误.②迭代器有效性的延续 :交换后,原容器的迭代器仍然有效,但现在指向另一个容器的元素.例如:
vector v1{1,2,3};
vector v2{4,5,6};
auto it = v1.begin(); // 指向1
v1.swap(v2);
cout << *it << endl; // 现在指向4(原v2的第一个元素)
释放内存的技巧 :可以通过和空容器交换,快速释放原容器的内存(因为空容器的容量为0,交换后原容器的内存会被销毁):vector v{1,2,3,4};
vector().swap(v); // 交换后v变为空,原内存被释放
💡 典型使用场景①快速交换两个容器 :无需拷贝元素,适合需要频繁交换数据的场景.
②高效释放内存 :与空容器交换,快速回收容器占用的内存(比
clear()+shrink_to_fit()更高效).③函数返回值优化 :在函数中返回大容器时,通过
swap可以避免拷贝(C++11 后已被移动语义替代,但仍有兼容性场景).
5.6 operator[]介绍(重点)
这是C++标准库中std::vector::operator[]的官方文档说明.📌 核心功能
operator[]是vector中最常用的元素访问操作符 ,用于直接获取容器中第n个位置(从 0 开始计数)的元素的引用,时间复杂度为O(1).
🔹函数签名(两个重载版本)// 非 const 版本:可读写元素
reference operator[] (size_type n);
// const 版本:仅可读元素
const_reference operator[] (size_type n) const;
非 const 版本 :当vector是可修改的(非 const 对象)时调用,返回元素的普通引用,可通过该引用修改元素值.
const 版本 :当vector是只读的(const 对象)时调用,返回元素的 const 引用,仅能读取元素值,不能修改.
🔹关键对比:operator[]vsvector::at()文档中特别强调了这两个相似函数的核心区别,这是高频考点:
| 特性 | operator[] |
vector::at() |
|---|---|---|
| 边界检查 | ❌ 无 | ✅ 有 |
| 越界行为 | 未定义(崩溃、数据损坏等) | 抛出 out_of_range 异常 |
| 效率 | 更高(无检查开销) | 稍低(需检查边界) |
| 使用场景 | 确定索引不会越界时(如循环遍历) | 索引可能越界时(如用户输入的索引) |
🔹参数与返回值
参数n:元素的位置(无符号整数类型size_type),从 0 开始计数(第一个元素位置为 0,第二个为 1,依此类推).
返回值 :非 const 容器:返回元素的普通引用 (
reference),可读写.const 容器:返回元素的const 引用 (
const_reference),仅可读.
⚠️ 重要注意事项①越界风险极高 :
operator[]不做边界检查,若n >= size()(越界),会触发未定义行为 (如程序崩溃、内存损坏、数据错误等).正确做法:若无法确保索引合法,应使用
vector::at()替代.②无符号参数的坑 :
n是无符号整数(size_type),不能传入负数(否则会溢出成极大的正数,导致越界).③空容器禁止调用 :若
vector为空(size() == 0),任何n都会越界,必须避免.
💡 使用场景
优先用operator[]:在索引明确合法的场景(如固定范围的循环遍历),追求最高效率。
必须用at():在索引可能来自外部输入、计算结果不确定的场景,保证程序稳定性。
6. vector迭代器失效问题(重点)
迭代器的主要作用就是让算法能够不用关心底层数据结构,其底层实际就是一个指针,或者是对指针进行了封装,比如:vector的迭代器就是原生态指针T*.因此迭代器失效,实际就是迭代器底层对应指针所指向的空间被销毁了,而使用一块已经被释放的空间,造成的后果是程序崩溃(即如果继续使用已经失效的迭代器,程序可能会崩溃).
6.1 vector可能会导致其迭代器失效的操作有:会引起其底层空间改变的操作
cpp
#include <iostream>
#include <vector>
using namespace std;
int main()
{
vector<int> v{1,2,3,4,5,6};
auto it = v.begin();
// 将有效元素个数增加到100个,多出的位置使用8填充,操作期间底层会扩容
// v.resize(100, 8);
// reserve的作用就是改变扩容大小但不改变有效元素个数,操作期间可能会引起底层容
量改变
// v.reserve(100);
// 插入元素期间,可能会引起扩容,而导致原空间被释放
// v.insert(v.begin(), 0);
// v.push_back(8);
// 给vector重新赋值,可能会引起底层容量改变
v.assign(100, 8);
/*
出错原因:以上操作,都有可能会导致vector扩容,也就是说vector底层原理旧空间被释
放掉,而在打印时,it还使用的是释放之间的旧空间,在对it迭代器操作时,实际操作的是一块
已经被释放的空间,而引起代码运行时崩溃。
解决方式:在以上操作完成之后,如果想要继续通过迭代器操作vector中的元素,只需给
it重新赋值即可。把下面这一行代码加上就解决了
/* it = v.begin();执行完修改容器的操作后,重新获取迭代器,让迭代器指向新内存*/
while(it != v.end())
{
cout<< *it << " " ;
++it;
}
cout<<endl;
return 0;
}
6.2 vector可能会导致其迭代器失效的操作有:指定位置元素的删除操作--erase
cpp
#include <iostream>
#include <vector>
using namespace std;
int main()
{
int a[] = { 1, 2, 3, 4 };
vector<int> v(a, a + sizeof(a) / sizeof(int));
// 使用find查找3所在位置的iterator
vector<int>::iterator pos = find(v.begin(), v.end(), 3);
// 删除pos位置的数据,导致pos迭代器失效。
v.erase(pos);
cout << *pos << endl; // 此处会导致非法访问
return 0;
}
erase删除pos位置元素后,pos位置之后的元素会往前搬移,没有导致底层空间的改变,理论上讲迭代器不应该会失效,但是:如果pos刚好是最后一个元素,删完之后pos刚好是end的位置,而end位置是没有元素的,那么pos就失效了.因此删除vector中任意位置上元素时,vs就认为该位置迭代器失效了.下面提供解决方法!
cpp
//方法一
/*v.erase(pos);*/ //将这一行注释掉
// 核心修复:用erase的返回值更新迭代器,pos指向删除后的下一个有效元素
if(pos != v.end()){ // 严谨性:判断找到元素后再删除,防止find返回end()空迭代器
pos = v.erase(pos);
}
cout << *pos << endl; // 此时pos是有效迭代器,指向原3的下一个元素(4),访问无问题
//方法二
v.erase(pos); // 删除pos位置的元素,pos失效
// 核心修复:删除/注释掉对失效迭代器的解引用操作,避免非法访问
// cout << *pos << endl;
for(int num : v){
cout << num << " ";
}
//方法三
vector<int> v{1,2,3,3,4,3,5};
// 循环删除所有值为3的元素:正确写法
for(vector<int>::iterator it = v.begin(); it != v.end(); ){
if(*it == 3){
it = v.erase(it); // 关键:删除后用返回值更新迭代器,不执行++it
}else{
++it; // 不删除时,正常自增
}
}
for(int num : v) cout << num << " ";
return 0;
6.3 删除vector中所有的偶数请问下面哪个代码是正确的为什么?
cpp
#include <iostream>
#include <vector>
using namespace std;
int main()
{
vector<int> v{ 1, 2, 3, 4 };
auto it = v.begin();
while (it != v.end())
{
if (*it % 2 == 0)
v.erase(it);
++it;
}
return 0;
}
cpp
#include <iostream>
#include <vector>
using namespace std;
int main()
{
vector<int> v{ 1, 2, 3, 4 };
auto it = v.begin();
while (it != v.end())
{
if (*it % 2 == 0)
it = v.erase(it);
else
++it;
}
return 0;
}
答案是第二个:因为erase 也会迭代器失效,失效的迭代器就不能再使用了.要重新赋值更新这个迭代器才能使用.所以要改成it = v.erase(it);
6.4 Linux下g++编译器对迭代器失效的检测并不是非常严格处理也没有vs下极端
cpp
// 1.扩容之后,迭代器已经失效了,程序虽然可以运行,但是运行结果已经不对了
int main()
{
vector<int> v{1,2,3,4,5};
for(size_t i = 0; i < v.size(); ++i)
cout << v[i] << " ";
cout << endl;
auto it = v.begin();
cout << "扩容之前,vector的容量为: " << v.capacity() << endl;
//通过reserve将底层空间设置为100,目的是为了让vector的迭代器失效
v.reserve(100);
cout << "扩容之后,vector的容量为: " << v.capacity() << endl;
// 经过上述reserve之后,it迭代器肯定会失效,在vs下程序就直接崩溃了,但是linux下不会
//虽然可能运行,但是输出的结果是不对的
while(it != v.end())
{
cout << *it << " ";
++it;
}
cout << endl;
return 0;
}
/*程序输出:
1 2 3 4 5
扩容之前,vector的容量为: 5
扩容之后,vector的容量为: 100
0 2 3 4 5 409 1 2 3 4 5*/
// 2. erase删除任意位置代码后,linux下迭代器并没有失效
// 因为空间还是原来的空间,后序元素往前搬移了,it的位置还是有效的
#include <vector>
#include <algorithm>
int main()
{
vector<int> v{1,2,3,4,5};
vector<int>::iterator it = find(v.begin(), v.end(), 3);
v.erase(it);
cout << *it << endl;
while(it != v.end())
{
cout << *it << " ";
++it;
}
cout << endl;
return 0;
}
/*程序可以正常运行,并打印:
4
4 5*/
// 3: erase删除的迭代器如果是最后一个元素,删除之后it已经超过end
// 此时迭代器是无效的,++it导致程序崩溃
int main()
{
vector<int> v{1,2,3,4,5};
// vector<int> v{1,2,3,4,5,6};
auto it = v.begin();
while(it != v.end())
{
if(*it % 2 == 0)
v.erase(it);
++it;
}
for(auto e : v)
cout << e << " ";
cout << endl;
return 0;
}
powershell
//使用第一组数据时,程序可以运行
root@VM-0-3-ubuntu:g++ testVector.cpp -std=c++11
root@VM-0-3-ubuntu:./a.out
1 3 5
//使用第二组数据时,程序最终会崩溃
root@VM-0-3-ubuntu:vim testVector.cpp
root@VM-0-3-ubuntu:g++ testVector.cpp -std=c++11
root@VM-0-3-ubuntu:./a.out
Segmentation fault
//从上述三个例子中可以看到:SGI STL中,迭代器失效后,代码并不一定会崩溃,但是运行
结果肯定不对,如果it不在begin和end范围内,肯定会崩溃的。
6.5 与vector类似string在插入+扩容操作+erase之后迭代器也会失效
cpp
#include <string>
void TestString()
{
string s("hello");
auto it = s.begin();
// 放开之后代码会崩溃,因为resize到20会string会进行扩容
// 扩容之后,it指向之前旧空间已经被释放了,该迭代器就失效了
// 后序打印时,再访问it指向的空间程序就会崩溃
//s.resize(20, '!');
while (it != s.end())
{
cout << *it;
++it;
}
cout << endl;
it = s.begin();
while (it != s.end())
{
it = s.erase(it);
// 按照下面方式写,运行时程序会崩溃,因为erase(it)之后
// it位置的迭代器就失效了
// s.erase(it);
++it;
}
}
7. 关于vector类一些oj算法题
7.1只出现一次的数字

cpp
class Solution {
public:
int singleNumber(vector<int>& nums) {
int result = 0;
for (int num : nums) {
result ^= num;
}
return result;
}
};
7.2只出现一次数字||

cpp
class Solution {
public:
int singleNumber(vector<int>& nums) {
int result = 0;
// 遍历32位整数的每一位
for (int i = 0; i < 32; ++i) {
int count = 0;
for (int num : nums) {
// 检查当前数的第i位是否为1
if ((num >> i) & 1) {
count++;
}
}
// 如果该位1的个数模3余1,说明目标数在该位为1
if (count % 3 == 1) {
result |= (1 << i);
}
}
return result;
}
};
7.3只出现一次数字的|||
cpp
class Solution {
public:
vector<int> singleNumber(vector<int>& nums) {
// 第一步:获取两个只出现一次的数字的异或值
long long xor_all = 0;
for (int num : nums) {
xor_all ^= num;
}
// 第二步:找到异或结果中最右边为1的位(最低位的1)
// 这个位表示两个目标数字在该位上有差异
// 使用补码技巧:diff = xor_all & (-xor_all)
// 注意:这里需要转换为long long以避免负数取反时的溢出问题
long long diff = xor_all & (-xor_all);
// 第三步:根据这个差异位将数组分成两组
int a = 0, b = 0;
for (int num : nums) {
if (num & diff) {
a ^= num; // 该位为1的数字在一组
} else {
b ^= num; // 该位为0的数字在另一组
}
}
return {a, b};
}
};
7.4杨辉三角

cpp
class Solution {
public:
vector<vector<int>> generate(int numRows) {
vector<vector<int>> vv;
vv.resize(numRows, vector<int>());
for(size_t i = 0; i < numRows; ++i)
{
vv[i].resize(i+1, 1);
}
for(size_t i = 2; i < vv.size(); ++i)
{
for(size_t j = 1; j < vv[i].size()-1; ++j)
{
vv[i][j] = vv[i-1][j] + vv[i-1][j-1];
}
}
return vv;
}
};
cpp
//我们从动态二维数组的角度去理解一下
// 以杨慧三角的前n行为例:假设n为5
void test2vector(size_t n)
{
// 使用vector定义二维数组vv,vv中的每个元素都是vector<int>
lisi::vector<bit::vector<int>> vv(n);
// 将二维数组每一行中的vecotr<int>中的元素全部设置为1
for (size_t i = 0; i < n; ++i)
vv[i].resize(i + 1, 1);
// 给杨慧三角出第一列和对角线的所有元素赋值
for (int i = 2; i < n; ++i)
{
for (int j = 1; j < i; ++j)
{
vv[i][j] = vv[i - 1][j] + vv[i - 1][j - 1];
}
}
}
//lisi::vector<bit::vector<int>> vv(n); 构造一个vv动态二维数组,vv中总共有n个元素,每个元素都是vector类型的,每行没有包含任何元素,如果n为5时如下所示:

7.5数组中出现次数超过一半的数字
cpp
class Solution {
public:
int MoreThanHalfNum_Solution(vector<int>& numbers) {
// 初始化候选数和计数器
int candidate = numbers[0];
int count = 1;
// 遍历数组进行投票
for (size_t i = 1; i < numbers.size(); ++i) {
if (numbers[i] == candidate) {
count++;
} else {
count--;
if (count == 0) {
candidate = numbers[i];
count = 1;
}
}
}
return candidate;
}
};
7.6电话号码的字母组合
cpp
class Solution {
public:
vector<string> letterCombinations(string digits) {
if (digits.empty()) return {};
// 数字到字母的映射
vector<string> mapping = {
"", "", "abc", "def", "ghi", "jkl", "mno",
"pqrs", "tuv", "wxyz"
};
vector<string> result;
string current;
backtrack(digits, mapping, 0, current, result);
return result;
}
private:
void backtrack(const string& digits, const vector<string>& mapping,
int index, string& current, vector<string>& result) {
// 如果已经处理完所有数字,将当前组合加入结果
if (index == digits.length()) {
result.push_back(current);
return;
}
// 获取当前数字对应的字母集合
int digit = digits[index] - '0';
const string& letters = mapping[digit];
// 遍历所有可能的字母
for (char letter : letters) {
// 选择当前字母
current.push_back(letter);
// 递归处理下一个数字
backtrack(digits, mapping, index + 1, current, result);
// 回溯,撤销选择
current.pop_back();
}
}
};
8. vector深度剖析
9. 使用memcpy拷贝问题
cpp
//假设模拟实现的vector中的reserve接口中,使用memcpy进行的拷贝,以下代码会发生什么问题?
int main()
{
lisi::vector<lisi::string> v;
v.push_back("1111");
v.push_back("2222");
v.push_back("3333");
return 0;
}
问题分析:
1️⃣memcpy是内存的二进制格式拷贝,将一段内存空间中内容原封不动的拷贝到另外一段内存空间中.
2️⃣如果拷贝的是自定义类型的元素,memcpy既高效又不会出错,但如果拷贝的是自定义类型元素,并且自定义类型元素中涉及到资源管理时,就会出错,因为memcpy的拷贝实际是浅拷贝.
结论:如果对象中涉及到资源管理时,千万不能使用memcpy进行对象之间的拷贝,因为memcpy是浅拷贝,否则可能会引起内存泄漏甚至程序崩溃.
10. vector的核心框架接口的模拟实现
cpp
#include <iostream>
#include <algorithm>
#include <cassert>
using namespace std;
// 模拟实现std::vector,类模板支持任意数据类型
template <class T>
class vector
{
public:
//1.迭代器相关(vector的迭代器是原生指针)
typedef T* iterator;
typedef const T* const_iterator;
// 普通迭代器
iterator begin() { return _start; }
iterator end() { return _finish; }
// const迭代器(只读,不可修改元素)
const_iterator begin() const { return _start; }
const_iterator end() const { return _finish; }
//2.构造函数/析构函数/拷贝构造/赋值重载(深拷贝)
// 无参构造:空容器
vector()
: _start(nullptr)
, _finish(nullptr)
, _end_of_storage(nullptr)
{}
// 带参构造:n个值为val的元素
vector(size_t n, const T& val = T())
: _start(nullptr)
, _finish(nullptr)
, _end_of_storage(nullptr)
{
reserve(n); // 提前开好空间,避免多次扩容
for (size_t i = 0; i < n; ++i)
{
push_back(val);
}
}
// 重载:处理int类型的n(比如vector<int> v(5, 10))
vector(int n, const T& val = T())
: vector((size_t)n, val)
{}
// 范围构造:支持任意迭代器区间 [first, last)
template <class InputIterator>
vector(InputIterator first, InputIterator last)
: _start(nullptr)
, _finish(nullptr)
, _end_of_storage(nullptr)
{
while (first != last)
{
push_back(*first);
++first;
}
}
// 拷贝构造:深拷贝 现代写法(最简洁高效)
vector(const vector<T>& v)
: _start(nullptr)
, _finish(nullptr)
, _end_of_storage(nullptr)
{
vector<T> tmp(v.begin(), v.end()); // 用v的元素构造临时对象
swap(tmp); // 交换临时对象和当前对象的成员,临时对象出作用域自动析构旧内存
}
// 赋值重载:深拷贝 现代写法(传值传参,自动拷贝)
vector<T>& operator=(vector<T> v)
{
swap(v);
return *this;
}
// 析构函数:释放申请的堆内存,防止内存泄漏
~vector()
{
if (_start) // 指针非空才释放
{
delete[] _start;
_start = _finish = _end_of_storage = nullptr;
}
}
//3.容量相关接口
// 返回有效元素个数
size_t size() const { return _finish - _start; }
// 返回容器的总容量
size_t capacity() const { return _end_of_storage - _start; }
// 判断容器是否为空
bool empty() const { return _start == _finish; }
// 扩容:只改变容量,不改变有效元素个数,扩容到n
void reserve(size_t n)
{
if (n > capacity())
{
size_t old_size = size(); // 保存旧的有效元素个数
// 1. 申请新内存
T* new_start = new T[n];
// 2. 拷贝旧内存的有效元素到新内存
if (_start) // 旧容器非空才拷贝
{
// 这里用赋值拷贝,支持所有类型
for (size_t i = 0; i < old_size; ++i)
{
new_start[i] = _start[i];
}
// 3. 释放旧内存
delete[] _start;
}
// 4. 更新指针
_start = new_start;
_finish = _start + old_size;
_end_of_storage = _start + n;
}
}
// 改变有效元素个数:n>size则尾插默认值,n<size则截断
void resize(size_t n, const T& val = T())
{
if (n < size()) // 缩小:直接改变finish指针即可,内存不释放
{
_finish = _start + n;
}
else // 增大:先扩容,再尾插元素
{
reserve(n);
while (_finish < _start + n)
{
*_finish = val;
++_finish;
}
}
}
//4.元素访问相关接口
// 普通访问:可读可写,支持[]下标访问
T& operator[](size_t pos)
{
assert(pos < size()); // 断言:防止下标越界
return _start[pos];
}
// const访问:只读,const对象调用
const T& operator[](size_t pos) const
{
assert(pos < size());
return _start[pos];
}
// 返回第一个元素
T& front() { assert(!empty()); return *_start; }
const T& front() const { assert(!empty()); return *_start; }
// 返回最后一个元素
T& back() { assert(!empty()); return *(_finish - 1); }
const T& back() const { assert(!empty()); return *(_finish - 1); }
//5.增删改查 核心接口
// 尾插元素:核心接口,满了自动扩容
void push_back(const T& val)
{
if (_finish == _end_of_storage) // 容量已满,需要扩容
{
// 扩容规则:空容器扩容到4,非空容器扩容2倍(STL标准规则)
size_t new_cap = capacity() == 0 ? 4 : capacity() * 2;
reserve(new_cap);
}
// 插入元素并更新finish指针
*_finish = val;
++_finish;
}
// 尾删元素:只改变finish指针,内存不释放(空间复用)
void pop_back()
{
assert(!empty());
--_finish;
}
// 指定位置插入元素:pos是迭代器,插入后原pos位置元素后移
iterator insert(iterator pos, const T& val)
{
assert(pos >= _start && pos <= _finish); // 迭代器合法性校验
// 满了先扩容
if (_finish == _end_of_storage)
{
// 扩容后迭代器会失效,需要计算pos的偏移量,更新pos
size_t offset = pos - _start;
size_t new_cap = capacity() == 0 ? 4 : capacity() * 2;
reserve(new_cap);
pos = _start + offset;
}
// 从后往前挪动元素,避免覆盖
iterator end = _finish;
while (end > pos)
{
*end = *(end - 1);
--end;
}
// 插入元素
*pos = val;
++_finish;
return pos; // 返回新插入元素的迭代器
}
// 指定位置删除元素:pos是迭代器,删除后原pos后元素前移
iterator erase(iterator pos)
{
assert(pos >= _start && pos < _finish); // 迭代器合法性校验
// 从pos往后挪动元素,覆盖要删除的元素
iterator begin = pos + 1;
while (begin < _finish)
{
*(begin - 1) = *begin;
++begin;
}
--_finish;
return pos; // 返回删除位置的下一个迭代器
}
// 清空容器:只清空有效元素,不释放内存(空间复用)
void clear() { _finish = _start; }
// 交换两个vector的内容:高效,仅交换指针
void swap(vector<T>& v)
{
std::swap(_start, v._start);
std::swap(_finish, v._finish);
std::swap(_end_of_storage, v._end_of_storage);
}
private:
iterator _start; // 指向容器的第一个有效元素
iterator _finish; // 指向容器的最后一个有效元素的下一个位置
iterator _end_of_storage;// 指向容器的内存空间的最后一个位置的下一个位置
};
//6.测试代码
void test_vector()
{
// 1. 无参构造 + 尾插 + 遍历
vector<int> v1;
v1.push_back(1);
v1.push_back(2);
v1.push_back(3);
cout << "v1的元素:";
for (size_t i = 0; i < v1.size(); ++i) { cout << v1[i] << " "; }
cout << endl; // 输出:1 2 3
// 2. 带参构造 + const迭代器遍历
vector<int> v2(5, 10);
cout << "v2的元素:";
for (vector<int>::const_iterator it = v2.begin(); it != v2.end(); ++it) { cout << *it << " "; }
cout << endl; // 输出:10 10 10 10 10
// 3. 拷贝构造 + 赋值重载
vector<int> v3(v1);
vector<int> v4;
v4 = v3;
cout << "v4的元素:";
for (auto e : v4) { cout << e << " "; } // 范围for遍历(支持迭代器即可)
cout << endl; // 输出:1 2 3
// 4. 容量和大小测试
cout << "v1的size:" << v1.size() << ",capacity:" << v1.capacity() << endl; // 3 4
v1.resize(5, 6);
cout << "v1 resize后:";
for (auto e : v1) { cout << e << " "; } // 1 2 3 6 6
cout << endl;
// 5. 插入和删除测试
v1.insert(v1.begin() + 2, 99);
cout << "v1插入后:";
for (auto e : v1) { cout << e << " "; } //1 2 99 3 6 6
cout << endl;
v1.erase(v1.begin() + 2);
cout << "v1删除后:";
for (auto e : v1) { cout << e << " "; } //1 2 3 6 6
cout << endl;
// 6. 首尾访问 + 尾删
cout << "v1的首元素:" << v1.front() << ",尾元素:" << v1.back() << endl;
v1.pop_back();
cout << "v1尾删后:";
for (auto e : v1) { cout << e << " "; } //1 2 3 6
cout << endl;
}
int main()
{
test_vector();
return 0;
}
核心知识点解析(必看)
✅1️⃣vector的底层结构(3 个指针)
本次实现的核心就是 STL 原生vector的底层逻辑,三个指针各司其职,所有接口都是基于这三个指针的移动实现:
_start:指向容器中第一个有效元素的地址
_finish:指向容器中最后一个有效元素的下一个位置
_end_of_storage:指向容器申请的内存空间的最后一个位置的下一个位置
核心公式:
有效元素个数:size() = _finish - _start
容器总容量:capacity() = _end_of_storage - _start
✅2️⃣核心特性:动态扩容机制
vector 是动态顺序表,和静态数组的核心区别就是自动扩容,本次实现的扩容规则是STL 标准规则:
当push_back插入元素时,如果_finish = _end_of_storage(容量已满),触发扩容空容器(capacity=0)第一次扩容,直接扩容到4非空容器扩容,直接扩容到原容量的 2 倍.
扩容本质:申请新内存 → 拷贝旧元素 → 释放旧内存 → 更新指针
注意:扩容后,原容器的迭代器 / 指针会失效,这是 vector 的特性
✅3️⃣深拷贝的两种现代写法(重点)
vector 的成员是指针,指向堆内存,必须实现深拷贝,否则会导致多个对象共享同一块内存,析构时重复释放内存崩溃.本次实现了两种最优的现代写法:
① 拷贝构造的现代写法
vector(const vector& v)
: _start(nullptr), _finish(nullptr), _end_of_storage(nullptr)
{
vector tmp(v.begin(), v.end()); // 用v的元素构造临时对象
swap(tmp); // 交换临时对象和当前对象的指针
}
原理:临时对象tmp申请独立内存,交换后当前对象拥有 tmp 的内存,tmp 出作用域析构时释放原对象的旧内存.
② 赋值重载的现代写法(极致简洁)
vector& operator=(vector v) // 传值传参,自动拷贝实参,生成临时对象v
{
swap(v);
return *this;
}
原理:传值传参时编译器自动调用拷贝构造,生成临时对象,交换后临时对象析构释放旧内存,一行代码实现深拷贝.
✅4️⃣迭代器失效问题
本次实现中已经处理了最常见的迭代器失效场景:
扩容导致迭代器失效:insert插入元素时,如果触发扩容,原迭代器会指向释放的旧内存,解决方案是:扩容前计算迭代器的偏移量,扩容后重新更新迭代器地址.
删除导致迭代器失效:erase删除元素后,返回删除位置的下一个迭代器,可以接收返回值更新迭代器,避免失效.
✅5️⃣接口清单(完整无遗漏)
本次实现包含std::vector的全部核心常用接口,满足日常开发的所有需求:
✅ 构造 / 析构:无参构造、n 个 val 构造、范围构造、拷贝构造、析构
✅ 赋值重载:深拷贝现代写法
✅ 迭代器:begin/end、const 版 begin/end(支持范围 for)
✅ 容量:size、capacity、empty、reserve(扩容)、resize(改有效元素)
✅ 访问:operator []、front、back(均有 const 版)
✅ 修改:push_back、pop_back、insert、erase、clear、swap
敬请期待下一篇文章内容-->C++中list类介绍!

















