目录
📌 一、std::vector 核心特性
📌 二、常用函数详解(附代码示例)
1. 构造函数
2. 元素访问
3. 容量操作
4. 修改操作
📌 三、性能分析与优化建议
1. 动态扩容机制
2. 随机访问 vs. 插入/删除性能
3. 迭代器失效问题
📌 四、常见陷阱与解决方案
1. 未初始化元素访问
2. 不必要的拷贝
📌 五、典型应用场景
1. 动态数组
2. 算法实现
3. 二维数组模拟
🧠 总结
C++从入门到入土学习导航_c++学习进程-CSDN博客
📌 一、std::vector
核心特性
std::vector
是 C++ STL 中最常用的 序列式容器,其底层基于 动态数组 实现,具有以下核心特性:
特性 |
说明 |
动态扩容 |
内存自动增长(默认翻倍策略),无需手动管理内存。 |
连续存储 |
元素存储在连续内存块中,支持指针偏移和随机访问。 |
随机访问 |
时间复杂度为 O(1),可通过下标或 at() 访问任意元素。 |
尾部操作高效 |
push_back() /pop_back() 时间复杂度为 O(1)(扩容时为 O(n))。 |
迭代器支持 |
提供随机访问迭代器,兼容 STL 算法(如 sort 、find )。 |
📌 二、常用函数详解(附代码示例)
1. 构造函数
#include <vector>
#include <iostream>
int main() {
// 1. 默认构造空 vector
std::vector<int> v1;
// 2. 构造指定大小和初始值
std::vector<int> v2(5, 100); // 5 个 100
// 3. 迭代器构造
std::vector<int> v3(v2.begin(), v2.end()); // 复制 v2 的内容
// 4. 初始化列表(C++11)
std::vector<int> v4 = {1, 2, 3, 4, 5};
// 打印所有 vector 内容
for (int i : v4) {
std::cout << i << " ";
}
return 0;
}
2. 元素访问
std::vector<int> v = {10, 20, 30, 40, 50};
// 1. 下标访问(不检查越界)
std::cout << "v[2] = " << v[2] << "\n"; // 输出 30
// 2. at()(带边界检查)
std::cout << "v.at(3) = " << v.at(3) << "\n"; // 输出 40
// 3. front() / back()
std::cout << "front: " << v.front() << "\n"; // 输出 10
std::cout << "back: " << v.back() << "\n"; // 输出 50
// 4. data()(C++11,返回指针)
int* ptr = v.data();
std::cout << "*ptr = " << *ptr << "\n"; // 输出 10
3. 容量操作
std::vector<int> v = {1, 2, 3};
// 1. size() / capacity()
std::cout << "size: " << v.size() << ", capacity: " << v.capacity() << "\n";
// 2. reserve() 预分配内存
v.reserve(10); // 扩容到至少 10 个元素的空间
std::cout << "capacity after reserve: " << v.capacity() << "\n";
// 3. resize() 调整元素个数
v.resize(5, 0); // 调整为 5 个元素,不足用 0 填充
for (int i : v) {
std::cout << i << " "; // 输出 1 2 3 0 0
}
std::cout << "\n";
// 4. shrink_to_fit()(C++11)减少多余容量
v.shrink_to_fit();
std::cout << "capacity after shrink_to_fit: " << v.capacity() << "\n";
4. 修改操作
std::vector<int> v = {1, 2, 3, 4, 5};
// 1. 尾部插入/删除
v.push_back(6); // 插入到末尾
v.pop_back(); // 删除末尾元素
// 2. emplace_back()(C++11,原地构造对象)
v.emplace_back(7); // 更高效的插入(避免临时对象)
// 3. 插入/删除任意位置
auto it = v.insert(v.begin() + 2, 99); // 在第 3 个位置插入 99
std::cout << "Inserted value: " << *it << "\n"; // 输出 99
v.erase(v.begin() + 1); // 删除第 2 个元素
// 4. 清空
v.clear();
std::cout << "size after clear: " << v.size() << "\n"; // 输出 0
📌 三、性能分析与优化建议
1. 动态扩容机制
- 默认策略:扩容时分配 2 倍当前容量 的新内存,拷贝旧数据,释放旧内存。
- 性能影响:频繁扩容会导致:
- 内存碎片:频繁分配/释放内存。
- 拷贝开销:大对象拷贝成本高。
- 优化方案:
- 预分配内存:使用
reserve()
避免多次扩容。
- 批量插入:优先使用
emplace_back()
而非 push_back()
。
// 示例:预分配内存减少扩容次数
std::vector<int> v;
v.reserve(1000); // 一次性分配 1000 个元素的空间
for (int i = 0; i < 1000; ++i) {
v.push_back(i); // 无需扩容
}
2. 随机访问 vs. 插入/删除性能
操作类型 |
时间复杂度 |
说明 |
随机访问 |
O(1) |
利用指针偏移直接访问元素。 |
尾部插入/删除 |
O(1)(平均) |
默认扩容时为 O(n)。 |
中间插入/删除 |
O(n) |
需移动元素,性能较差。 |
查找(find) |
O(n) |
无序数据需遍历;有序数据可用二分法。 |
// 示例:查找特定值
int target = 3;
auto it = std::find(v.begin(), v.end(), target);
if (it != v.end()) {
std::cout << "Found at index: " << it - v.begin() << "\n";
} else {
std::cout << "Not found\n";
}
3. 迭代器失效问题
- 扩容导致迭代器失效:
push_back()
触发扩容后,所有迭代器失效。
- 插入/删除导致迭代器失效:
insert()
/erase()
后,迭代器需重新获取。
// 错误示例:扩容后迭代器失效
std::vector<int> v = {1, 2, 3};
auto it = v.begin();
v.push_back(4); // 可能触发扩容,it 失效
std::cout << *it << "\n"; // 未定义行为!
// 正确做法:重新获取迭代器
it = v.begin();
std::cout << *it << "\n"; // 安全
📌 四、常见陷阱与解决方案
1. 未初始化元素访问
- 错误示例:
reserve()
不初始化元素,直接访问会导致未定义行为。
- 正确做法:使用
resize()
初始化元素。
std::vector<int> v;
v.reserve(10); // 仅分配内存,未初始化元素
std::cout << v[5]; // 未定义行为!
v.resize(10); // 初始化 10 个元素为 0
std::cout << v[5]; // 合法,输出 0
2. 不必要的拷贝
- 推荐使用
emplace_back()
:直接构造对象,避免临时对象拷贝。
struct MyStruct {
MyStruct(int a, int b) : x(a), y(b) {}
int x, y;
};
std::vector<MyStruct> v;
// push_back 会创建临时对象再拷贝
v.push_back(MyStruct(1, 2)); // 两次构造(一次临时,一次拷贝)
// emplace_back 原地构造,避免拷贝
v.emplace_back(1, 2); // 一次构造
📌 五、典型应用场景
1. 动态数组
- 替代静态数组,支持运行时调整大小。
- 适用于数据量不确定的场景(如读取用户输入、动态数据收集)。
std::vector<int> nums;
int n;
while (std::cin >> n) {
nums.push_back(n); // 动态添加元素
}
2. 算法实现
- 与 STL 算法无缝兼容(如
sort
、find
、transform
)。
#include <algorithm>
std::vector<int> v = {5, 2, 4, 1, 3};
std::sort(v.begin(), v.end()); // 排序
std::reverse(v.begin(), v.end()); // 反转
3. 二维数组模拟
std::vector<std::vector<int>> pascal(5);
for (int i = 0; i < 5; ++i) {
pascal[i].resize(i + 1, 1);
for (int j = 1; j < i; ++j) {
pascal[i][j] = pascal[i-1][j] + pascal[i-1][j-1];
}
}
🧠 总结
特性 |
适用场景 |
优势 |
注意事项 |
动态扩容 |
数据量不确定 |
自动管理内存 |
频繁扩容影响性能 |
连续存储 |
需要随机访问 |
缓存命中率高 |
中间插入/删除慢 |
尾部操作高效 |
栈/队列模拟 |
插入/删除 O(1) |
扩容时迭代器失效 |
迭代器支持 |
STL 算法兼容 |
与 sort 、find 等无缝对接 |
插入/删除后需更新迭代器 |