一、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 各种操作的时间复杂度如下:

  • 随机访问([] 和 at ()):O (1)

  • 尾部插入 / 删除(push_back ()/pop_back ()):O (1)( amortized,平均时间)

  • 中间插入 / 删除:O (n)

  • 查找元素:O (n)(未排序情况下)

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内存中构造,无拷贝