一、vector 容器概述
vector 是 C++ 标准模板库(STL)中
最用的序列容器之一,它基于动态数组实现,能够存储同类型元素并支持高效的随机访问。自 C++98 标准首次引入以来,vector 凭借其灵活的内存管理和优秀的性能表现,成为了 C++ 开发者处理动态数据集合的首选工具。
与静态数组相比,vector 的核心优势在于自动内存管理—— 它会根据元素数量动态调整内部存储空间,无需开发者手动分配和释放内存。这种特性使得 vector 在处理元素数量不确定的场景时尤为便捷,同时保持了数组的随机访问效率。
vector 的核心特性可以概括为:
连续的内存空间分配,支持随机访问
动态扩容机制,自动管理内存
尾部插入 / 删除操作效率高
中间插入 / 删除操作可能导致大量元素移动
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66
| #include <iostream> #include <vector> #include <algorithm>
int main() { // 1. 多种构造方式 std::vector<int> vec1; // 空vector std::vector<int> vec2(3, 10); // 3个元素,均为10 std::vector<int> vec3 = {1, 2, 3, 4, 5}; // 列表初始化 std::vector<int> vec4(vec3.begin() + 1, vec3.end() - 1); // 迭代器范围构造
// 2. 元素添加操作 vec1.push_back(20); // 尾部添加 vec1.push_back(30); vec1.emplace_back(40); // 直接构造(C++11) vec2.insert(vec2.begin() + 1, 20); // 指定位置插入
// 3. 元素访问操作 std::cout << "vec3第一个元素: " << vec3[0] << std::endl; std::cout << "vec3第二个元素: " << vec3.at(1) << std::endl; std::cout << "vec3最后一个元素: " << vec3.back() << std::endl;
// 4. 迭代器遍历 std::cout << "vec3所有元素: "; for (std::vector<int>::iterator it = vec3.begin(); it != vec3.end(); ++it) { std::cout << *it << " "; } std::cout << std::endl;
// 5. 范围for循环遍历(C++11) std::cout << "vec2所有元素: "; for (const auto& elem : vec2) { std::cout << elem << " "; } std::cout << std::endl;
// 6. 元素修改操作 vec3[2] = 100; // 下标修改 vec3.at(3) = 200; // at()修改 vec3.pop_back(); // 删除尾部元素
// 7. 容器大小操作 std::cout << "vec3当前大小: " << vec3.size() << std::endl; std::cout << "vec3当前容量: " << vec3.capacity() << std::endl; vec3.reserve(10); // 预留容量 std::cout << "vec3预留后容量: " << vec3.capacity() << std::endl;
// 8. 元素查找与排序 auto find_it = std::find(vec3.begin(), vec3.end(), 100); if (find_it != vec3.end()) { std::cout << "找到元素100,位置索引: " << (find_it - vec3.begin()) << std::endl; } std::sort(vec3.begin(), vec3.end()); // 排序 std::cout << "排序后vec3: "; for (const auto& elem : vec3) { std::cout << elem << " "; } std::cout << std::endl;
// 9. 清空与交换 vec4.clear(); // 清空元素 vec1.swap(vec2); // 交换两个vector内容
return 0; }
|
代码输出结果:
1 2 3 4 5 6 7 8 9 10
| vec3第一个元素: 1 vec3第二个元素: 2 vec3最后一个元素: 5 vec3所有元素: 1 2 3 4 5 vec2所有元素: 10 20 10 10 vec3当前大小: 4 vec3当前容量: 5 vec3预留后容量: 10 找到元素100,位置索引: 2 排序后vec3: 1 2 100 200
|
注意:下标访问([])不进行边界检查,速度略快;at () 方法会进行边界检查,更安全但有轻微性能开销。
二、vector 迭代器操作
迭代器是访问 vector 元素的抽象接口,提供了统一的遍历方式:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| std::vector<int> vec = {1, 2, 3, 4, 5};
// 1. 正向迭代器 for (std::vector<int>::iterator it = vec.begin(); it != vec.end(); ++it) { std::cout << *it << " "; }
// 2. 常量正向迭代器(C++11 auto简化写法) for (auto it = vec.cbegin(); it != vec.cend(); ++it) { std::cout << *it << " "; // 不能通过it修改元素 }
// 3. 反向迭代器 for (auto it = vec.rbegin(); it != vec.rend(); ++it) { std::cout << *it << " "; // 反向遍历 }
|
在 C++11 及以上标准中,还可以使用范围 for 循环简化遍历:
1 2 3
| for (const auto& elem : vec) { std::cout << elem << " "; }
|
三、vector 性能分析
3.1 时间复杂度
vector 各种操作的时间复杂度如下:
3.2 性能优化策略
预分配容量:使用 reserve () 提前分配已知的所需容量
1 2 3 4 5 6 7 8 9 10 11 12
| // 优化前:可能发生多次扩容 std::vector<int> vec1; for (int i = 0; i < 1000; ++i) { vec1.push_back(i); }
// 优化后:一次分配,性能更优 std::vector<int> vec2; vec2.reserve(1000); // 预分配容量 for (int i = 0; i < 1000; ++i) { vec2.push_back(i); }
|
批量操作代替多次单个操作:使用 insert () 一次性插入多个元素,减少操作次数
避免不必要的拷贝:传递 vector 时优先使用引用(&)或常量引用(const&)
1 2 3 4 5 6 7 8 9
| // 不推荐:会导致vector拷贝,O(n)时间复杂度 void process_vector(std::vector<int> v) { // 处理逻辑 }
// 推荐:仅传递引用,O(1)时间复杂度 void process_vector(const std::vector<int>& v) { // 处理逻辑 }
|
四、常见误区与注意事项
4.1 迭代器失效问题
vector 扩容会导致内存地址变化,从而使之前获取的迭代器、指针和引用失效:
1 2 3 4 5 6 7 8 9
| std::vector<int> vec = {1, 2, 3}; int* ptr = &vec[0]; // 指向第一个元素的指针 auto it = vec.begin(); // 迭代器
vec.push_back(4); // 可能触发扩容,导致ptr和it失效
// 危险:使用已失效的指针或迭代器 std::cout << *ptr << std::endl; // 未定义行为 std::cout << *it << std::endl; // 未定义行为
|
避免策略:
扩容操作后重新获取迭代器
预先 reserve () 足够容量避免扩容
避免在循环中保存迭代器
4.2 错误的清空方式
clear () 方法只会清空元素(修改 size),但不会释放内存(capacity 保持不变):
1 2 3
| std::vector<int> vec(100); vec.clear(); std::cout << vec.size() << " " << vec.capacity() << std::endl; // 输出0 100
|
如果需要彻底释放内存,可以使用 "交换技巧":
1
| std::vector<int>().swap(vec); // 交换后vec容量变为0
|
4.3 不必要的元素复制
向 vector 中添加自定义类型元素时,应注意避免不必要的拷贝:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| // 自定义类型 class MyClass { public: MyClass() { std::cout << "构造函数" << std::endl; } MyClass(const MyClass&) { std::cout << "拷贝构造函数" << std::endl; } };
// 低效方式:会产生临时对象和拷贝 std::vector<MyClass> vec; MyClass obj; vec.push_back(obj); // 调用拷贝构造
// 高效方式:直接在vector中构造(C++11及以上) vec.emplace_back(); // 直接在vector内存中构造,无拷贝
|