1. 概述
-
vector 是可变大小的序列容器,属于 C++ 标准模板库(STL)。
-
它封装了动态数组,元素在内存中连续存储,因此支持随机访问(下标或迭代器)。
-
位于 <vector> 头文件中,命名空间 std。
cpp
#include <vector>
using namespace std;
核心特点:
-
尾部插入/删除元素高效(摊销常数时间 O(1))。
-
在中间或头部插入/删除元素较慢(O(n) 因为要移动元素)。
-
容器自动管理内存,当元素数量超出当前容量时,会重新分配一块更大的内存,并复制/移动原有元素。
-
提供 size()(实际元素个数)和 capacity()(已分配内存能容纳的元素个数)。
2. 声明与初始化
| vector<T> v; | 空 vector | vector<int> v; |
| vector<T> v(n); | 包含 n 个值初始化的元素(int 则为 0) | vector<int> v(10); |
| vector<T> v(n, val); | 包含 n 个值为 val 的元素 | vector<int> v(5, 3); // [3,3,3,3,3] |
| vector<T> v{1,2,3}; (C++11) | 列表初始化 | vector<int> v{1,2,3}; |
| vector<T> v(begin, end); | 用迭代器区间构造 | vector<int> v2(v1.begin(), v1.end()); |
| vector<T> v(other); | 拷贝构造 | vector<int> v2(v1); |
| vector<T> v(move(other)); | 移动构造(C++11) | vector<int> v2(move(v1)); |
3. 大小与容量
| size() | 返回当前元素个数 |
| empty() | 是否为空 |
| capacity() | 当前已分配内存可容纳的元素个数(≥ size) |
| reserve(n) | 预留至少 n 个元素的内存(若 n > capacity 则重新分配,否则无变化) |
| shrink_to_fit() (C++11) | 释放多余容量,使 capacity 等于 size(非强制,可能不执行) |
| resize(n, val) | 改变大小为 n,若变大则用 val 填充新元素(默认值初始化) |
cpp
vector<int> v;
v.reserve(100); // capacity >= 100, size 仍为 0
v.push_back(42); // 不会触发扩容
cout << v.size() << " " << v.capacity(); // 1 至少100
v.shrink_to_fit(); // 尝试让 capacity 收缩到 1
4. 元素访问
| operator[] | 下标访问,不检查越界 | int x = v[0]; |
| at() | 下标访问,检查越界(抛 out_of_range 异常) | int x = v.at(0); |
| front() | 首元素引用 | v.front() = 10; |
| back() | 尾元素引用 | int last = v.back(); |
| data() (C++11) | 返回底层数组指针 | int* p = v.data(); |
注意:operator[] 和 at() 都要求索引 < size,否则 operator[] 导致未定义行为,at() 抛异常。
5. 修改操作
| push_back(val) | 尾部添加元素 | 均摊 O(1) |
| pop_back() | 删除尾部元素 | O(1) |
| insert(pos, val) / insert(pos, n, val) / insert(pos, begin, end) | 在迭代器 pos 前插入元素 | O(n)(移动元素) |
| erase(pos) / erase(first, last) | 删除一个或一段元素 | O(n) |
| clear() | 删除所有元素,size 变 0,capacity 不变 | O(n)(调用析构) |
| swap(other) | 交换两个 vector 的内容(非常快,只交换指针) | O(1) |
| assign(n, val) / assign(begin, end) | 替换所有元素 | O(n) |
| emplace_back(args…) (C++11) | 在尾部原位构造元素,避免拷贝/移动 | 均摊 O(1) |
| emplace(pos, args…) | 在 pos 前原位构造 | O(n) |
cpp
vector<string> v;
v.push_back("hello"); // 拷贝构造
v.emplace_back(3, 'A'); // 直接构造 "AAA"(更高效)
v.insert(v.begin(), "first");
v.erase(v.begin() + 1);
6. 迭代器
vector 提供随机访问迭代器,支持 +、-、++、–、[] 等操作。
| begin() / end() | 正向迭代器 |
| rbegin() / rend() | 反向迭代器 |
| cbegin() / cend() (C++11) | 常量正向迭代器 |
cpp
for (auto it = v.begin(); it != v.end(); ++it) cout << *it << " ";
for (int x : v) cout << x << " "; // 范围 for 更简洁
for (auto rit = v.rbegin(); rit != v.rend(); ++rit) // 反向遍历
7. 比较操作
vector 重载了关系运算符(==、!=、<、<=、>、>=),按字典序比较元素。
cpp
vector<int> a{1,2,3}, b{1,2,3}, c{1,2,4};
a == b; // true
a < c; // true (第三元素 3 < 4)
8. 内存与性能特点
8.1 扩容机制
-
当 size() == capacity() 并试图添加元素时,vector 会重新分配内存。
-
新容量通常为原容量的 1.5 倍或 2 倍(具体由库实现决定,如 GCC 2 倍,VS 1.5 倍)。
-
重新分配时,所有元素会移动(C++11 后优先移动而非拷贝)到新内存,原迭代器、引用、指针全部失效。
-
可以通过 reserve() 预分配内存避免多次扩容,提高性能。
8.2 时间效率
-
随机访问:O(1)
-
尾部插入/删除:均摊 O(1)
-
中间/头部插入/删除:O(n)
-
查找(无专用函数,需用 find 算法):O(n)
8.3 空间效率
-
元素连续存储,无额外指针开销(对比 list),空间利用率高。
-
但 capacity 可能大于 size,造成内存浪费。可使用 shrink_to_fit() 释放多余容量(非强制)。
9. 迭代器失效问题
修改 vector 结构(增加或删除元素)可能导致迭代器、引用、指针失效:
-
插入元素(insert、push_back、emplace_back、resize):
-
若未触发重新分配,插入点之后的迭代器失效(但引用仍有效?严格说:push_back 不导致重新分配时,之前迭代器仍有效?C++标准:push_back 若不重新分配,则所有迭代器、引用仍有效。更细致的规则可查标准,但最好认为任何插入/删除都可能使迭代器失效。)
-
若触发重新分配,全部失效。
-
-
删除元素(erase、pop_back、clear):
-
删除点之后的所有迭代器失效。
-
安全做法:在修改后重新获取迭代器,或使用下标索引而非迭代器。
10. vector 与其他容器的对比
| 内存结构 | 连续 | 分段连续 | 双向链表(非连续) |
| 随机访问 | O(1) 快 | O(1) 较快 | 不支持(需遍历) |
| 头部插入/删除 | O(n) 慢 | O(1) 快 | O(1) 快 |
| 尾部插入/删除 | 均摊 O(1) | O(1) | O(1) |
| 中间插入/删除 | O(n) | O(n) (但比 vector 稍快) | O(1) 如果知道位置 |
| 迭代器类型 | 随机访问 | 随机访问 | 双向 |
| 内存开销 | 低(连续) | 略高(多段) | 高(每个元素有前后指针) |
选择建议:
-
需要频繁随机访问,且主要在尾部操作 → vector
-
需要在头尾两端高效操作 → deque
-
需要在中间频繁插入删除,不需要随机访问 → list 或 forward_list
11. 完整示例
cpp
#include <iostream>
#include <vector>
#include <algorithm> // for find, sort
using namespace std;
int main() {
// 初始化
vector<int> v = {5, 2, 8, 1, 9};
// 尾部操作
v.push_back(3);
v.pop_back();
// 访问
cout << "First: " << v.front() << ", Last: " << v.back() << endl;
cout << "Size: " << v.size() << ", Capacity: " << v.capacity() << endl;
// 插入
v.insert(v.begin() + 2, 99); // 在索引 2 前插入 99
// 删除
v.erase(v.begin() + 1); // 删除索引 1 的元素
// 排序
sort(v.begin(), v.end());
// 查找
auto it = find(v.begin(), v.end(), 8);
if (it != v.end())
cout << "Found 8 at position " << (it – v.begin()) << endl;
// 遍历
cout << "Elements: ";
for (int x : v) cout << x << " ";
cout << endl;
// 清除
v.clear();
cout << "After clear, size = " << v.size() << endl;
// 预分配内存
v.reserve(1000);
cout << "Capacity after reserve: " << v.capacity() << endl;
return 0;
}
12. 注意事项
下标越界:使用 operator[] 时保证索引 < size,否则 UB。更安全用 at()。
避免频繁扩容:如果事先知道大致元素个数,用 reserve() 预分配。
使用 emplace_back 代替 push_back:对于需要构造临时对象的场景,emplace_back 更高效(直接传递构造参数)。
不要在遍历时修改容器结构(如插入、删除),否则迭代器失效。可以使用下标循环并从后往前删除,或使用 erase-remove 惯用法。
cpp
// 删除所有值为 0 的元素(正确做法)
v.erase(remove(v.begin(), v.end(), 0), v.end());
vector<bool> 特化:vector<bool> 并非存储 bool 数组,而是位压缩,因此它的 operator[] 返回一个代理类,不能取地址,且行为与一般 vector 不同。若需要真正的 bool 数组,考虑 deque<bool> 或 vector<char>。
以上是 vector 容器的核心知识。如需进一步了解底层实现或 C++11/14/17/20 的新特性(如 std::vector::erase 返回迭代器、std::pmr::vector 等),可查阅 cppreference.com – std::vector。



