C++中std::vector使用超级详解
·
一、std::vector 简介
std::vector 是 C++ STL 中最常用的动态数组容器,特点是:
- 支持 随机访问
- 内存是 一块连续的空间
- 自动扩容,支持添加和删除元素
#include <vector>
#include <iostream>
二、常见用法
1. 初始化
std::vector<int> v1; // 空向量
std::vector<int> v2(5); // 5 个默认值为 0 的元素
std::vector<int> v3(5, -1); // 5 个值为 -1 的元素
std::vector<int> v4 = {1, 2, 3, 4}; // 初始化列表
2. 添加元素
v1.push_back(10); // 添加一个元素到末尾
3. 遍历元素
for (size_t i = 0; i < v1.size(); ++i)
std::cout << v1[i] << " ";
for (int x : v1)
std::cout << x << " ";
for (auto it = v1.begin(); it != v1.end(); ++it)
std::cout << *it << " ";
4. 常用操作
v1.size(); // 当前元素个数
v1.empty(); // 是否为空
v1.clear(); // 清空所有元素
v1.back(); // 返回最后一个元素
v1.front(); // 返回第一个元素
三、resize() vs reserve() 的区别
| 函数 | 作用 | 影响大小 | 影响容量 | 初始化元素 |
|---|---|---|---|---|
resize(n) |
直接改变 size,增大时会初始化元素 | ✅ | ✅ | ✅ 默认值 |
reserve(n) |
预留容量,不改变 size,不初始化元素 | ❌ | ✅ | ❌ |
std::vector<int> v;
v.reserve(5); // 容量变为5,但 size 还是0
v.resize(3); // 现在 size 变为3,初始化3个元素
四、性能分析
1. 添加元素性能
std::vector<int> v;
for (int i = 0; i < 1e6; ++i) {
v.push_back(i); // 会不断扩容(通常以 2 倍增长)
}
- 每次扩容会分配新内存并拷贝旧数据
- 为避免频繁扩容,可提前
reserve(n)
2. 与 std::deque 比较
| 操作 | vector |
deque |
|---|---|---|
| push_back | ✅ 快 | ✅ 快 |
| push_front | ❌ 非常慢 | ✅ 快 |
| 随机访问 | ✅ 高效(连续内存) | ✅ 稍慢(分块) |
| 遍历性能 | ✅ 最优 | ❌ 缓存命中较差 |
3. 与 std::list 比较
| 操作 | vector |
list |
|---|---|---|
| 随机访问 | ✅ 支持 | ❌ 不支持 |
| 中间插入 | ❌ 慢(移动元素) | ✅ 快(常数时间) |
| 遍历性能 | ✅ 好 | ❌ 差(非局部访问) |
五、注意事项
| 注意点 | 建议 |
|---|---|
| 内存连续 | 不要随意返回 vector 的 data() 指针给外部长期持有 |
| 插入性能 | 插入大量数据前建议 reserve() |
| 指针/引用失效 | 扩容时旧地址会失效 |
resize() 构造新对象 |
对类对象注意构造/析构成本 |
push_back 后地址失效 |
扩容时原指针、引用都失效!避免用裸指针 |
| 频繁插入中间位置慢 | 插入/删除中间元素为 O(n),性能差 |
clear() 不回收内存 |
清空元素但不会减少容量 |
shrink_to_fit() 不可靠 |
提交释放容量,但具体行为平台相关 |
指针失效示例
std::vector<int> v = {1, 2, 3};
int* ptr = &v[0];
v.push_back(4); // 如果扩容,ptr 可能失效!
六、综合使用示例
1. 保存类对象
struct Point {
float x, y;
};
std::vector<Point> points;
points.emplace_back(1.0f, 2.0f);
2. 用作二维数组
std::vector<std::vector<int>> matrix(3, std::vector<int>(4, 0));
// 3 行 4 列,全部初始化为 0
3. 与 std::shared_ptr 配合
std::vector<std::shared_ptr<Point>> vec;
vec.push_back(std::make_shared<Point>());
4. 使用 std::move 提高性能:
std::vector<std::string> v;
std::string s = "hello";
v.push_back(std::move(s)); // 避免拷贝
5. 使用 std::vector<bool> 的陷阱:
- 它是位压缩优化版,不是普通的 bool 数组
- 有时会导致不能获取引用/指针
建议避免使用它,或用 std::vector<char> 替代。
6. 快速可视化 size 与 capacity 增长
std::vector<int> v;
for (int i = 0; i < 100; ++i) {
v.push_back(i);
std::cout << "Size: " << v.size()
<< ", Capacity: " << v.capacity() << std::endl;
}
7. 固定长度数组的模拟:
std::vector<int> data(100, 0); // 100 个元素,初始为0
8. 避免重复分配:
std::vector<std::string> names;
names.reserve(1000);
for (...) {
names.emplace_back(...); // 高效
}
std::vector 是一个自动管理内存的容器,但它的内存释放行为可能不是你预期的:即使你调用了 clear(),它也不会释放 capacity 对应的内存。
七、std::vector对象内存释放
std::vector 常见的释放方式总结如下。
| 方法 | 作用 | 内存是否释放 |
|---|---|---|
clear() |
清除所有元素 | ❌ 不释放内存(capacity 保留) |
shrink_to_fit() |
请求释放多余内存 | ✅ 释放未使用容量(非强制) |
swap() |
与空 vector 交换,强制释放 | ✅✅ 推荐方式 |
std::vector<T>().swap(vec) |
简写版强制释放 | ✅✅ 推荐方式 |
示例演示
示例 1:clear() 不释放内存
std::vector<int> v(10000);
std::cout << "Before clear: size = " << v.size()
<< ", capacity = " << v.capacity() << "\n";
v.clear();
std::cout << "After clear: size = " << v.size()
<< ", capacity = " << v.capacity() << "\n";
输出可能是:
Before clear: size = 10000, capacity = 10000
After clear: size = 0, capacity = 10000
示例 2:推荐做法 — 与空 vector 交换
std::vector<int> v(10000);
std::vector<int>().swap(v); // 强制释放所有内存
// v.size() == 0, v.capacity() == 0
示例 3:使用 shrink_to_fit()
std::vector<int> v(10000);
v.clear();
v.shrink_to_fit(); // 请求释放 capacity,但不是强制的
注意:shrink_to_fit() 是 non-binding request,标准库实现可以选择忽略这个请求。
总结建议
| 目标 | 推荐操作 |
|---|---|
| 清除数据,但保留内存用于重用 | clear() |
| 清除数据 + 建议释放内存 | clear(); shrink_to_fit(); |
| 清除数据 + 强制释放所有内存 | std::vector<T>().swap(vec); |
高级应用建议
- 在循环中避免反复构造销毁大 vector,建议:
vec.clear();+reserve(n);预分配 - 如果
vector是类成员,析构中使用 swap 强制释放空间更彻底 - 用于缓存图像帧 / 点云数据时,
vector.swap()能快速释放大块内存
八、小结
| 特性 | 结论 |
|---|---|
| 内存模型 | 连续 |
| 访问方式 | 支持随机访问 |
| 插入效率 | 后端高效,中间慢 |
| 替代容器建议 | 若需头部插入:用 deque;频繁中间操作:用 list |
| 调优建议 | reserve() 可提升性能;避免频繁扩容 |
| 连续内存,兼容 C 数组 | 中间插入慢,扩容成本高 |
| 标准接口统一 | 需要手动管理扩容策略 |
与 Eigen、OpenCV cv::Mat 等兼容性高 |
无法支持双端插入 |
更多推荐

所有评论(0)