C++ vector 从入门到上手:核心基本用法全解析
std::vector 是 C++ STL 中使用频率最高的容器之一,它是一个动态数组,可以自动扩容,几乎可以替代大部分手动管理的数组场景。
下面从零基础到能熟练使用,一次性把 vector 的核心用法讲清楚。
1. 引入 vector
#include <vector> // 必须包含这个头文件
#include <iostream>
using namespace std; // 方便演示(生产代码建议别全局 using)
2. 基本声明与初始化方式(最常用 6 种)
// 方式1:空 vector
vector<int> v1;
// 方式2:指定初始大小(元素默认初始化为 0)
vector<int> v2(10); // 10 个 0
// 方式3:指定大小 + 初始值
vector<int> v3(10, 7); // 10 个 7
// 方式4:用 initializer_list 初始化(C++11+ 最常用)
vector<int> v4 = {1, 2, 3, 4, 5};
vector<int> v5{10, 20, 30}; // 统一初始化语法
// 方式5:拷贝构造
vector<int> v6 = v4; // 拷贝 v4
vector<int> v7(v4.begin(), v4.end()); // 范围构造
// 方式6:从数组构造
int arr[] = {100, 200, 300};
vector<int> v8(arr, arr + 3);
vector<int> v9(begin(arr), end(arr)); // C++11+ 更推荐
3. 常用成员函数速查表(背下来就够用)
| 功能 | 写法 | 时间复杂度 | 说明 / 注意事项 |
|---|---|---|---|
| 元素个数 | v.size() | O(1) | 当前元素数量 |
| 是否为空 | v.empty() | O(1) | 比 size()==0 更语义化 |
| 容量 | v.capacity() | O(1) | 已分配的内存空间(字节数 / sizeof(T)) |
| 访问(安全) | v.at(i) | O(1) | 越界抛出 out_of_range 异常 |
| 访问(不安全但快) | v[i] / v.front() / v.back() | O(1) | 不检查越界,生产代码慎用 |
| 添加元素(末尾) | v.push_back(x) | 均摊 O(1) | 最常用,可能会触发扩容 |
| 移除末尾元素 | v.pop_back() | O(1) | 不返回被删除元素 |
| 清空 | v.clear() | O(n) | size 变为 0,但 capacity 不变 |
| 插入(任意位置) | v.insert(it, val) | O(n) | 插入到迭代器 it 之前 |
| 删除(任意位置) | v.erase(it) | O(n) | 删除迭代器指向的元素,返回下一个有效迭代器 |
| 调整大小 | v.resize(n) / v.resize(n, val) | O(n) | 变大时填充 val(默认 0),变小时截断 |
| 预分配空间 | v.reserve(n) | O(n) | 提前分配空间,避免多次扩容 |
| 交换两个 vector | v1.swap(v2) | O(1) | 极快,常用于清空 + 释放内存 |
4. 迭代器与遍历方式(C++11 之后最推荐)
vector<int> v = {10, 20, 30, 40, 50};
// 方式1:下标遍历(最直观)
for (size_t i = 0; i < v.size(); ++i) {
cout << v[i] << " ";
}
// 方式2:迭代器遍历(传统写法)
for (vector<int>::iterator it = v.begin(); it != v.end(); ++it) {
cout << *it << " ";
}
// 方式3:C++11 范围 for(最推荐,简洁安全)
for (int x : v) {
cout << x << " ";
}
// 方式4:C++17 结构化绑定(如果元素是 pair 等)
for (auto [key, value] : map_vec) { ... }
// 方式5:auto 自动推导(最常用写法)
for (auto x : v) cout << x << " "; // 拷贝
for (const auto& x : v) cout << x << " "; // 推荐(不拷贝)
for (auto& x : v) x *= 2; // 可修改原容器
5. 常用操作代码示例(直接复制可用)
#include <iostream>
#include <vector>
#include <algorithm> // 用于 sort 等
using namespace std;
int main() {
vector<int> v;
// 1. 添加元素
v.push_back(10);
v.push_back(20);
v.push_back(5);
v.emplace_back(30); // 比 push_back 更高效(原地构造)
// 2. 遍历输出
cout << "原始内容: ";
for (const auto& x : v) cout << x << " ";
cout << "\n";
// 3. 插入
v.insert(v.begin() + 1, 15); // 在下标 1 位置插入 15
// 4. 删除
v.erase(v.begin()); // 删除第一个元素
// 5. 排序
sort(v.begin(), v.end());
// 6. 查找(需要 <algorithm>)
auto it = find(v.begin(), v.end(), 20);
if (it != v.end()) {
cout << "找到 20 了!位置:" << (it - v.begin()) << endl;
}
// 7. 清空但保留容量
v.clear();
cout << "清空后 size = " << v.size() << ", capacity = " << v.capacity() << endl;
// 8. 释放内存(当你真的不需要了)
vector<int>().swap(v); // 技巧:用空 vector 交换
return 0;
}
6. 性能与注意事项(面试 + 生产必知)
- 扩容机制
vector 容量不够时会重新分配更大的内存(通常 1.5~2 倍),然后把旧元素拷贝/移动过去
→ 导致均摊 O(1),但单次可能 O(n) - reserve vs resize
reserve(n):只分配空间,不构造元素(推荐提前知道大概大小时使用)resize(n):分配空间 + 构造元素(n > size 时填充默认值)
- 避免频繁插入/删除头部
→ O(n) 复杂度,考虑std::deque或std::list - emplace_back vs push_back
emplace_back原地构造,效率更高(尤其元素是复杂对象时)
- 迭代器失效规则(非常重要!) 操作 迭代器是否失效
push_back(不触发扩容) 已有迭代器有效push_back(触发扩容) 全部失效insert / erase(中间) 插入/删除点之后的迭代器失效pop_back仅最后一个迭代器失效clear / resize全部失效 安全遍历删除写法:
for (auto it = v.begin(); it != v.end(); ) {
if (*it % 2 == 0) {
it = v.erase(it); // erase 返回下一个有效迭代器
} else {
++it;
}
}
7. 总结:vector 使用口诀
- 小数据量、顺序访问、频繁尾部增删 → vector 是首选
- 提前知道大概大小 → 先
reserve - 需要频繁插入删除 → 考虑
deque或list - 遍历优先用
for (const auto& x : v) - 删除时小心迭代器失效
- 追求极致性能时用
emplace_back而不是push_back
如果你现在想练习哪一部分(比如 vector + 算法、vector 实现二维动态数组、vector 作为函数参数、常见面试题等),可以直接告诉我,我可以继续给出针对性的代码和讲解。