C++学习之旅【C++Vector类介绍—入门指南与核心概念解析】


🔥承渊政道: 个人主页
❄️个人专栏: 《C语言基础语法知识》 《数据结构与算法》

《C++知识内容》《Linux系统知识》

✨逆境不吐心中苦,顺境不忘来时路! 🎬 博主简介:

引言:前篇文章,小编已经介绍了关于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的元素(比如 intstring、自定义类等).
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 标准算法(如 sortfindfor_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() vs front()

文档里特意强调了这一点:
vector::front() → 返回第一个元素的引用 (直接拿到元素值,比如 int val = v.front();).
vector::begin() → 返回指向第一个元素的随机访问迭代器 (用来定位/遍历元素,比如 auto it = v.begin(); int val = *it;).
⚠️ 重要注意事项

如果 vector 是空的(v.empty() == true),调用 begin() 返回的迭代器不能被解引用 (因为没有元素可以指向,解引用会导致未定义行为,比如程序崩溃).

📋 参数与返回值
参数 :无参数,直接调用即可.
返回值

vectorconst 对象,返回 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).

📋 参数与返回值
参数 :无参数,直接调用即可.
返回值 :

vectorconst 对象,返回 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() 指向的是实际元素(最后一个元素),因此可以安全解引用;但如果容器为空,则不能解引用.

📋 参数与返回值
参数 :无参数,直接调用即可.
返回值

vectorconst 对象,返回 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 容器遍历的统一规范.

📋 参数与返回值
参数 :无参数,直接调用即可.
返回值 :

vectorconst 对象,返回 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() vs capacity()

文档特别强调了两者的区别,这是理解 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() vs size() vs max_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() 变为 ncapacity() 变为扩容后的容量).
🔹关键对比:resize() vs reserve()

这是最容易混淆的两个函数,必须明确区分:

函数 作用 是否修改元素数量 是否影响容量
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() vs resize()

这是最容易混淆的两个函数,必须明确区分:

函数 作用 是否修改元素数量 是否影响容量
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 效率更低(链表插入仅需修改指针,无需移动元素).若需频繁在中间插入,建议优先使用listdeque.
🔹参数详解

参数 含义
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 效率更低(链表删除仅需修改指针,无需移动元素).若需频繁在中间删除,建议优先使用 listdeque.

元素销毁 :被删除的元素会调用其析构函数完成销毁,若元素包含动态内存(如指针、自定义对象),需确保内存已正确释放以避免泄漏.
🔹参数详解

参数 含义
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(即模板参数 TAlloc 完全相同),通过引用传递.
无返回值 :直接修改两个容器的内部状态.
🔹行为逻辑

内容交换 :交换后,当前容器的元素变为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[] vs vector::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类介绍!


相关推荐
Filotimo_1 小时前
在java后端开发中,LEFT JOIN的用法
java·开发语言·windows
Swift社区1 小时前
在Swift中实现允许重复的O(1)随机集合
开发语言·ios·swift
四谎真好看1 小时前
JavaWeb学习笔记(Day05)之请求响应
笔记·学习·学习笔记·javaweb
hetao17338371 小时前
2026-01-21~22 hetao1733837 的刷题笔记
c++·笔记·算法
2301_797312261 小时前
学习Java43天
java·开发语言
2501_901147832 小时前
零钱兑换——动态规划与高性能优化学习笔记
学习·算法·面试·职场和发展·性能优化·动态规划·求职招聘
冰暮流星2 小时前
javascript之do-while循环
开发语言·javascript·ecmascript
头发还没掉光光3 小时前
Linux网络之IP协议
linux·运维·网络·c++·tcp/ip
2501_944424123 小时前
Flutter for OpenHarmony游戏集合App实战之连连看路径连线
android·开发语言·前端·javascript·flutter·游戏·php