> 技术文档 > 【C++】STL详解(三)—vector使用手册:不看你会后悔

【C++】STL详解(三)—vector使用手册:不看你会后悔


坚持用 清晰易懂的图解 + 代码语言,让每个知识点变得简单!
🚀呆头个人主页详情
🌱 呆头个人Gitee代码仓库
📌 呆头详细专栏系列
座右铭: “不患无位,患所以立。”在这里插入图片描述


【C++】STL详解(三)—vector使用手册:不看你会后悔

  • 摘要
  • 目录
  • 一、vector的介绍
  • 二、vector的使用
    • 1.vector的定义方式
      • 构造一个空的 vector(任意类型)
      • 构造一个含有 n 个元素的 vector,每个元素的值都是 val
      • 拷贝构造函数,用已有的 vector 构造新的 vector
      • 用区间 [first, last) 中的元素构造 vector
    • 2.迭代器的使用
      • begin和end
      • rbegin和rend
    • 3.空间的增长问题
      • size和capacity
      • reserve和reszie
      • empty
    • 4.vector增删查改
      • push_back和pop_back
      • insert和erase
      • swap
      • 元素访问operator[ ]
    • 5.迭代器失效问题
      • 示例1
      • 示例2

vector文档参考----------请点击


摘要

🚀 欢迎来到《C++修炼之路》!

这里是C++程序员的成长乐园,带你领略从面向对象到现代C++的精彩世界。我们将>用简洁的代码和生动的案例,助你掌握C++核心精髓。

🔍 专栏亮点:

  • 现代C++特性解析(C++11/14/17)
  • STL源码剖析与实战应用
  • 内存管理与性能优化技巧

💡 收获预期:
✔️ 写出更健壮的C++代码
✔️ 深入理解面向对象设计
✔️ 掌握模板编程基础

📌 编程箴言:

“好的C++代码就像好酒,需要时间沉淀。”

(正文开始👇)—— + 本篇讲解vector的使用


目录

一、vector的介绍

【C++】STL详解(三)—vector使用手册:不看你会后悔

  1. 定义: vector 是一个表示 可变大小数组 的序列容器

  2. 存储方式: 与数组一样,vector 使用 连续内存空间 存储元素,因此可以通过下标随机访问,时间复杂度为 O(1)

  3. 动态扩容: 与普通数组不同,vector 的大小可以动态改变。当空间不足时,会分配新的更大内存,把原有元素拷贝过去,再释放旧空间。

  4. 空间策略:vector预留额外空间 来减少频繁扩容。不同实现的扩容策略不同,但通常是以倍数方式增长,从而保证 均摊插入复杂度为 O(1)

  5. 性能特点:

    • 访问元素效率高(支持随机访问)。
    • 在末尾插入/删除效率高。
    • 在中间或开头插入/删除效率低(需要移动大量元素)。

👉 一句话总结:
vector 是 C++ 中最常用的容器,本质是一个能自动扩容的动态数组,既有数组的高效访问,又比数组更灵活。

二、vector的使用

1.vector的定义方式

【C++】STL详解(三)—vector使用手册:不看你会后悔

构造一个空的 vector(任意类型)

注意:这只是一个示例,注意是任意类型,不止如下的类型

vector<int> v1; //构造int类型的空容器vector<double> v2; //构造double类型的空容器vector<char> v3; //构造char类型的空容器vector<string> v4; //构造string类型的空容器

构造一个含有 n 个元素的 vector,每个元素的值都是 val

vector v1(n个元素,值val);

vector<int> v1(10,2);vector<double> v2(10,0.0);vector<char> v3(10,\'d\');

拷贝构造函数,用已有的 vector 构造新的 vector

vector<int> v1(v2); //拷贝构造int类型的v2容器的复制品

用区间 [first, last) 中的元素构造 vector

vector<int> v2(v1.begin(), v1.end()); //使用迭代器拷贝构造v2容器的某一段内容

【C++】STL详解(三)—vector使用手册:不看你会后悔


2.迭代器的使用

vector 中,迭代器的底层实现通常就是一个普通指针,因为 vector 的元素存储在连续的内存空间里,用指针就能完成迭代器的所有功能。因此在 vector 阶段,迭代器本质上等同于指针。

但在其他容器(如 listmap)中,元素存储方式不同,不一定是连续内存,这时迭代器并不是单纯的指针,而是一个 封装了指针行为的类对象,通过运算符重载来模拟“像指针一样使用”。


begin和end

begin函数可以得到容器中第一个元素的正向迭代器,通过end函数可以得到容器中最后一个元素的下一个位置的正向迭代器。
【C++】STL详解(三)—vector使用手册:不看你会后悔

int main(){vector<int> v(6, 6);vector<int>::iterator it = v.begin();while (it != v.end()){cout << *it << \" \" ;++it;}cout << endl;return 0;}

此处仅展示普通vector对象的迭代器使用,因为容器的迭代器的使用都是相通的【C++】STL详解(三)—vector使用手册:不看你会后悔


rbegin和rend

rbegin函数可以得到容器中最后一个元素的反向迭代器,通过rend函数可以得到容器中第一个元素的前一个位置的反向迭代器【C++】STL详解(三)—vector使用手册:不看你会后悔

int main(){vector<int> v1;v1.push_back(5);v1.push_back(2);v1.push_back(0);vector<int>::reverse_iterator rit = v1.rbegin();while (rit != v1.rend()){cout << *rit << \" \";++rit;}return 0;}

运行结果如下:【C++】STL详解(三)—vector使用手册:不看你会后悔


3.空间的增长问题

size和capacity

通过size函数获取当前容器中的有效元素个数,通过capacity函数获取当前容器的最大容量。

#include#includeusing namespace std;void test01(){vector<int> v1(6, 6);cout << \"size:\" << v1.size() << endl;//获取当前容器有效数据个数cout << \"capacity:\" << v1.capacity() << endl;//获取当前容器最大容量}int main(){test01();return 0;}

运行结果如下:
【C++】STL详解(三)—vector使用手册:不看你会后悔


reserve和reszie

函数 作用 规则 reserve 改变容器的 容量 (capacity) 1. 当所给值 > 当前 capacity 时,扩容到该值。
2. 当所给值 ≤ 当前 capacity 时,不做任何操作。 resize 改变容器的 有效元素个数 (size) 1. 当所给值 > 当前 size 时,扩展 size 到该值,新增元素为指定值(默认 0)。
2. 当所给值 < 当前 size 时,缩小 size 到该值,超出部分元素被移除。
#include#includeusing namespace std;void test01(){vector<int> v1(6, 6);cout << \"size: \" << v1.size() << endl;//获取当前容器有效数据个数 6cout << \"capacity: \" << v1.capacity() << endl;//获取当前容器最大容量 6v1.reserve(20);//修改容器最大容量为20cout << \"size: \" << v1.size() << endl;//6cout << \"capacity: \" << v1.capacity() << endl;//20v1.resize(10);cout << \"size: \" << v1.size() << endl;//10,剩余空间默认为0cout << \"capacity: \" << v1.capacity() << endl;//10v1.resize(15,8);cout << \"size: \" << v1.size() << endl;//15,剩余空间补8cout << \"capacity: \" << v1.capacity() << endl;//15}int main(){test01();return 0;}

运行结果如下:
【C++】STL详解(三)—vector使用手册:不看你会后悔


empty

通过empty函数判断当前容器是否为空。

#include#includeusing namespace std;void test02(){vector<int> v2(6, 6);vector<int> v3;cout << \"v2: \" << v2.empty() << endl;cout << \"v3: \" << v3.empty() << endl;}int main(){test02();return 0;}

运行结果如下:
【C++】STL详解(三)—vector使用手册:不看你会后悔


4.vector增删查改

push_back和pop_back

通过push_back函数对容器进行尾插,pop_back函数对容器进行尾删。

#include#includeusing namespace std;void test03(){vector<int> v1;v1.push_back(5);v1.push_back(2);v1.push_back(0);v1.push_back(1);v1.push_back(3);v1.push_back(1);v1.push_back(4);v1.pop_back();v1.pop_back();}int main(){//test01();//test02();test03();return 0;}

代码运行如下:
【C++】STL详解(三)—vector使用手册:不看你会后悔
【C++】STL详解(三)—vector使用手册:不看你会后悔


insert和erase

通过insert函数可以在所给迭代器pos位置插入一个或多个元素,通过erase函数可以删除所给迭代器pos位置的元素,或删除所给迭代器区间内的所有元素(左闭右开)。

#include#includeusing namespace std;void test04(){vector<int> v;v.push_back(1);v.push_back(2);v.push_back(3);v.push_back(4);v.insert(v.begin(), 0); //在容器开头插入0v.insert(v.begin(), 5, -1); //在容器开头插入5个-1v.erase(v.begin()); //删除容器中的第一个元素v.erase(v.begin(), v.begin() + 5); //删除在该迭代器区间内的元素(左闭右开)return 0;}int main(){//test01();//test02();//test03();test04();return 0;}

运行结果如下:
【C++】STL详解(三)—vector使用手册:不看你会后悔
【C++】STL详解(三)—vector使用手册:不看你会后悔

以上是按位置进行插入或删除元素的方式,若要按值进行插入或删除(在某一特定值位置进行插入或删除),则需要用到find函数

find函数:
find函数共三个参数,前两个参数确定一个迭代器区间(左闭右开),第三个参数确定所要寻找的值。
find函数在所给迭代器区间寻找第一个匹配的元素,并返回它的迭代器,若未找到,则返回所给的第二个参数。

注意: find函数是在算法模块(algorithm)当中实现的,不是vector的成员函数。

#include#include#include  using namespace std;void test04(){vector<int> v;v.push_back(5);v.push_back(2);v.push_back(0);v.insert(v.begin(), 1); //在容器开头插入1vector<int>::iterator pos = find(v.begin(), v.end(), 2); //获取值为2的元素的迭代器v.insert(pos, 10); //在2的位置插入10pos = find(v.begin(), v.end(), 0); //获取值为0的元素的迭代器v.erase(pos); //删除0}int main(){//test01();//test02();//test03();test04();return 0;}

运行结果如下:
【C++】STL详解(三)—vector使用手册:不看你会后悔


swap

通过swap函数可以交换两个容器的数据空间,实现两个容器的交换。

#include#includeusing namespace std;void test05(){vector<int> v1(6, 6);vector<int> v2(6, 8);cout << \"v1的数据为:\"; for (auto e : v1){cout << e << \' \';}cout << endl;cout << \"v2的数据为:\";for (auto e : v2){cout << e << \' \';}cout << endl << endl;v1.swap(v2);cout << \"v1的数据为:\";for (auto e : v1){cout << e << \' \';}cout << endl;cout << \"v2的数据为:\";for (auto e : v2){cout << e << \' \';}cout << endl;}int main(){//test01();//test02();//test03();//test04();test05();return 0;}

运行结果如下:
【C++】STL详解(三)—vector使用手册:不看你会后悔


元素访问operator[ ]

vector当中实现了 [ ] 操作符的重载,因此我们也可以通过“下标+[ ]”的方式对容器当中的元素进行访问。

#include#includeusing namespace std;#include  void test06(){vector<int> v(10, 1);//使用“下标+[]”的方式遍历容器for (size_t i = 0; i < v.size(); i++){cout << v[i] << \" \";}cout << endl;}int main(){//test01();//test02();//test03();//test04();//test05();test06();return 0;}

运行结果如下:
【C++】STL详解(三)—vector使用手册:不看你会后悔


5.迭代器失效问题

迭代器的主要作用是屏蔽底层实现细节,使我们在操作不同容器时无需关心其内部数据结构。例如,在 vector 中,迭代器的底层本质上就是一个普通指针。

所谓迭代器失效,是指迭代器底层所依赖的指针指向的内存空间已经被释放或移动,导致该迭代器无法再正确访问数据。如果在失效后继续使用该迭代器,就会访问到一块已经无效的内存区域,从而可能引发程序异常甚至崩溃。

示例1

#include #include #include using namespace std;int main(){vector<int> v;v.push_back(1);v.push_back(2);v.push_back(3);v.push_back(4);v.push_back(5);//v: 1 2 3 4 5vector<int>::iterator pos = find(v.begin(), v.end(), 2); //获取值为2的元素的迭代器v.insert(pos, 10); //在值为2的元素的位置插入10//v: 1 10 2 3 4 5v.erase(pos); //删除元素2 ???error(迭代器失效)//v: 1 2 3 4 5return 0;}

这里 pos 指向容器中值为 2 的元素。此时容器内容为:

v: 1 2 3 4 5 ↑ pos

pos 所指元素(值为 2)之前插入一个 10,容器变为:

v: 1 10 2 3 4 5 ↑ pos

关键点

  • vector::insert 可能导致 扩容,扩容会使所有迭代器失效。
  • 即使没有扩容,插入操作也会导致插入位置及其之后的所有迭代器失效。
  • 因为 vector 是连续存储的,插入元素需要搬移后续元素到新位置。
  • 所以,posinsert 之后已经不再是合法迭代器
    这里继续使用 pos,但 pos 已经失效。
  • 它指向的内存位置已经被移动,不再代表原来的元素。
  • 访问或传入 STL 算法时会导致 未定义行为,可能崩溃,也可能表现异常。

正确写法

要避免失效,可以在 insert 后重新获取迭代器:

pos = find(v.begin(), v.end(), 2); // 重新定位v.erase(pos);

示例2

#include #include using namespace std;int main(){vector<int> v;for (size_t i = 1; i <= 6; i++){v.push_back(i);}vector<int>::iterator it = v.begin();while (it != v.end()){if (*it % 2 == 0) //删除容器当中的全部偶数{v.erase(it);}it++;}return 0;}

  • vector 中,erase(it) 会删除 it 所指元素,并返回一个指向被删元素后一个位置的新迭代器
  • 由于 vector 的底层是连续存储的,删除操作会导致当前位置及其后的所有迭代器失效

例如:

v: 1 2 3 4 5 6 ↑ it (指向2)erase(it) 后:v: 1 3 4 5 6 ↑ it (原来的指针已经失效!)

此时 it 不再合法,继续 it++ 就是 未定义行为,可能跳过元素、也可能崩溃。


✅ 正确写法

erase 的返回值更新迭代器

while (it != v.end()){ if (*it % 2 == 0) { it = v.erase(it); // 返回值是“下一个有效迭代器” } else { ++it; // 只有没删的时候才自增 }}

📢 如果你也喜欢这种“不呆头”的技术风格:
👁️ 【关注】 看一个非典型程序员如何用野路子解决正经问题
👍 【点赞】 给“不写八股文”的技术分享一点鼓励
🔖 【收藏】 把这些“奇怪但有用”的代码技巧打包带走
💬 【评论】 来聊聊——你遇到过最“呆头”的 Bug 是啥?
🗳️ 【投票】 您的投票是支持我前行的动力
技术没有标准答案,让我们一起用最有趣的方式,写出最靠谱的代码! 🎮💻