
在C++标准库中,std::vector 是一种动态数组类型,提供了灵活且高效的数组操作。std::vector 的迭代器与算法是其强大功能的重要组成部分。
vector 的迭代器是随机访问迭代器(Random Access Iterator),兼具指针的灵活性和容器的语义,支持 +/- 算术运算、[] 下标访问等操作。其核心作用是解耦容器与算法,使同一套算法(如 sort、find)能作用于不同容器(vector、deque)。
vector<int> 为例)类型 | 定义 | 特性 |
|---|---|---|
iterator | int* | 读写迭代器,支持修改元素 |
const_iterator | const int* | 只读迭代器,禁止修改元素(*it = 5 编译错误) |
reverse_iterator | 反向迭代器(从尾到头) | rbegin() 指向最后一个元素,rend() 指向第一个元素前 |
const_reverse_iterator | 反向只读迭代器 | 结合 cbegin() 使用,安全遍历常量容器 |
实战技巧:
cbegin()/cend()(C++11)替代 begin()/end(),避免意外修改for (auto it = vec.rbegin(); it != vec.rend(); ++it)操作 | 失效范围 | 安全处理方式 |
|---|---|---|
push_back | 若扩容,所有迭代器失效 | 捕获返回值(无返回值,需重新获取) |
insert(pos, val) | pos 及之后的迭代器失效 | 保存 insert 返回的新迭代器 |
erase(pos) | pos 失效,返回下一个有效迭代器 | it = vec.erase(it)(循环删元素必用) |
clear() | 所有迭代器失效 | 置为 end() |
经典案例:删除偶数元素
for (auto it = vec.begin(); it != vec.end(); ) {
if (*it % 2 == 0) {
it = vec.erase(it); // 关键:更新迭代器
} else {
++it;
}
}STL 算法通过迭代器区间 [first, last) 操作容器,vector 因其连续存储的特性,能完美适配所有随机访问算法。
算法 | 作用 | 示例代码 |
|---|---|---|
find | 查找元素 | auto it = find(vec.begin(), vec.end(), 42); |
count | 统计元素出现次数 | int cnt = count(vec.begin(), vec.end(), 0); |
binary_search | 二分查找(需有序) | bool exists = binary_search(vec.cbegin(), vec.cend(), 100); |
性能优化:对有序 vector 优先使用 lower_bound/upper_bound(时间复杂度 O (logN))
算法 | 作用 | 注意事项 |
|---|---|---|
sort | 排序 | 随机访问迭代器专属,vector 效率最高 |
remove | 移除元素(需配合 erase) | vec.erase(remove(vec.begin(), vec.end(), 0), vec.end()); |
transform | 元素转换 | transform(vec.begin(), vec.end(), vec.begin(), [](int x) { return x*2; }); |
误区警示:remove 不会真正删除元素,仅覆盖,需结合 erase 收缩容器
vector 的 emplace_back 配合迭代器,可避免不必要的拷贝:
struct BigObj { /* 大对象 */ };
vector<BigObj> vec;
auto it = vec.emplace(vec.begin(), 42); // 直接在头部构造对象,减少一次拷贝通过 vector 的 data() 获取原始指针,结合迭代器实现子数组视图:
int* subarray = vec.data() + 2; // 指向第3个元素
vector<int> view(subarray, subarray + 5); // 构造包含5个元素的视图注意:vector 本身非线程安全,迭代器在多线程中可能失效。安全方案:
const_iterator + 互斥锁reserve 预分配内存,减少扩容导致的迭代器失效在 GDB 中打印迭代器指向的元素:
(gdb) print *vec.begin() // 打印第一个元素
(gdb) print *(vec.end() - 1) // 打印最后一个元素insert/erase 的返回值)insert(pos, n, val) 替代多次 push_back(减少扩容次数)reserve 避免动态扩容(如 vec.reserve(1000))场景 | 低效做法 | 高效做法 | 时间复杂度 |
|---|---|---|---|
查找元素 | 手动遍历 | find + 二分查找(有序) | O(N) → O(logN) |
删除多个元素 | 多次 erase | remove + erase | O(N) |
构造新容器 | 循环 push_back | 算法初始化(如 vector<int> vec(generate_n(back_inserter(vec), 100, []{return rand();})) | O(N) |
const 的结合const vector<int> cvec = {1, 2, 3};
// 错误:cvec.begin() 返回 iterator,允许修改
// 正确:使用 cbegin() 获取 const_iterator
for (auto it = cvec.cbegin(); it != cvec.cend(); ++it) {
*it = 0; // 编译错误,保护常量容器
}vector 的「生命线」vector 的迭代器不仅是遍历工具,更是连接容器与算法的核心枢纽。掌握迭代器的失效规则、与算法的协同工作,以及结合移动语义、多线程的优化,能写出更高效、安全的代码。记住:迭代器的每一次移动,都在与内存对话,理解其背后的容器行为(如扩容、缩容),是进阶 C++ 程序员的必经之路。
实战 checklist:
✅ 遍历前检查容器是否为空(避免 end() 越界)
✅ 使用 erase 时保存返回的迭代器
✅ 对有序容器优先使用二分查找相关算法
✅ 常量容器强制使用 cbegin/cend
✅ 批量操作前调用 reserve 预分配内存
std::vector 的迭代器与标准库算法为处理动态数组提供了强大的工具。通过迭代器,可以方便地遍历和访问 std::vector 中的元素。而标准库算法则提供了高效且简洁的方法来操作这些元素,如查找、替换、排序等。理解并善用这些工具,可以显著提高C++编程的效率和代码的可读性。
using声明在模板编程中有着重要应用,如定义模板类型别名等。