摘要:stl迭代器的用法很重要,可以很大程度上便利编程。本文介绍了使用迭代器相关的函数进行填充,导航和初始化等操作,本文可以作为一个速查本。
C++ STL 迭代器操作:填充、迭代器导航与初始化
引言
STL 的全拼是 Standard Template Library,中文翻译为 “标准模板库” 。
它是 C++ 编程语言标准库的重要组成部分,提供了一系列可复用的通用组件。
核心组成部分:
STL 主要包含四大组件:
-
容器 - 用于存储数据的通用数据结构。
- 例如:
vector(动态数组),list(链表),map(关联数组),set(集合)等。
- 例如:
-
算法 - 用于操作容器中数据的函数模板。
- 例如:
sort(排序),find(查找),copy(复制)等。
- 例如:
-
迭代器 - 类似于指针的对象,用于在容器中遍历元素,是连接容器和算法的桥梁。
-
函数对象 - 行为类似函数的对象,可以作为算法的某种策略。
在 C++ 开发中,STL(标准模板库)是我们日常工作中不可或缺的工具。然而,面对容器操作时,常常会对各种填充方法、迭代器导航和初始化方式记不住,所以本文简要总结了一下日常用到的迭代器相关的函数。
一、Vector 指定范围填充的多种方式
1.1 使用 std::fill() 函数(推荐)
std::fill() 是填充容器范围的首选方法,它简洁高效且意图明确。
#include <algorithm>
#include <vector>
std::vector<int> vec = {1, 2, 3, 4, 5, 6, 7, 8, 9};
// 填充索引 [2, 5) 的范围为 99
std::fill(vec.begin() + 2, vec.begin() + 5, 99);
// 结果:1, 2, 99, 99, 99, 6, 7, 8, 9
1.2 使用 std::fill_n() 函数
当你需要从某个位置开始填充指定数量的元素时,std::fill_n() 更加合适。
// 从索引3开始,填充4个元素为77
std::fill_n(vec.begin() + 3, 4, 77);
// 结果:1, 2, 3, 77, 77, 77, 77, 8, 9
很像 string类的 substr(start, count) 方法。
1.3 手动循环填充
递归简单场景下可以直接使用循环:
for (int i = start; i < end; ++i) {
vec[i] = value;
}
1.4 重要注意事项
- 范围表示:始终使用左闭右开区间
[start, end) - 边界检查:确保索引不越界
- 性能考虑:标准库函数通常比手动循环更高效
二、迭代器位置访问与导航
2.1 访问下一个位置的三种方式
2.1.1 使用 std::next() 函数(推荐)
#include <iterator>
#include <vector>
std::vector<int> vec = {10, 20, 30, 40, 50};
auto it = vec.begin() + 1; // 指向20
// 获取下一个位置
auto next_it = std::next(it);
std::cout << *next_it; // 输出:30
// 指定步长前进
auto future_it = std::next(it, 3);
std::cout << *future_it; // 输出:50
2.1.2 使用 + 运算符
auto next_it = it + 1; // 下一个位置
auto future_it = it + 3; // 后面第三个位置
2.1.3 使用递增运算符
auto next_it = ++it; // 前置递增,it 和 next_it 都指向新位置
2.2 访问前一个位置
使用 std::prev() 函数可以安全地获取前一个位置:
auto it = vec.begin() + 3; // 指向40
auto prev_it = std::prev(it); // 指向30
auto prev_two = std::prev(it, 2); // 指向20
三、std::prev() 和 std::next() 的深入探讨
3.1 跨容器支持情况
| 容器类型 | 迭代器类别 | std::next() | std::prev() | + 运算符 |
|---|---|---|---|---|
std::vector | 随机访问 | ✅ | ✅ | ✅ |
std::list | 双向 | ✅ | ✅ | ❌ |
std::forward_list | 前向 | ✅ | ❌ | ❌ |
std::deque | 随机访问 | ✅ | ✅ | ✅ |
std::set/map | 双向 | ✅ | ✅ | ❌ |
3.2 在 List 中的使用
#include <list>
#include <iterator>
std::list<int> mylist = {10, 20, 30, 40, 50};
auto it = mylist.begin();
std::advance(it, 2); // 移动到30的位置
// list 不支持 + 运算符,但可以用 next/prev
auto prev_it = std::prev(it, 2); // 指向10
auto next_it = std::next(it, 2); // 指向50
下面是另外的一个迭代器操作函数 advance和next的区别
| 特性 | std::advance | std::next |
|---|---|---|
| 返回值 | 无 (void) | 新的迭代器 |
| 是否修改原迭代器 | ✅ 是 | ❌ 否 |
| 使用场景 | 移动现有迭代器 | 获取新迭代器而不修改原迭代器 |
| 性能 | 直接修改,可能更高效 | 需要创建副本 |
| 安全性 | 不检测边界 | 不检测边界 |
distance能检测两个迭代器的距离,但是前提是,迭代器本身没有失效。
3.3 在 Set/Map 中的使用
#include <set>
#include <iterator>
std::set<int> myset = {10, 20, 30, 40, 50};
auto it = myset.find(30);
if (it != myset.end()) {
auto prev_it = std::prev(it); // 指向20
auto next_it = std::next(it); // 指向40
}
四、std::list 初始化方法大全
4.1 基础初始化方式
#include <list>
// 1. 空列表
std::list<int> list1;
// 2. 指定大小
std::list<int> list2(5); // 5个元素,默认值0
std::list<int> list3(5, 42); // 5个元素,每个都是42
// 3. 初始化列表(推荐)
std::list<int> list4 = {1, 2, 3, 4, 5};
std::list<std::string> list5{"apple", "banana", "cherry"};
// 4. 从其他容器复制
std::list<int> original = {1, 2, 3};
std::list<int> list6(original); // 拷贝构造
std::list<int> list7(original.begin(), original.end()); // 迭代器范围
4.2 从不同类型容器初始化
// 从数组初始化
int arr[] = {10, 20, 30, 40};
std::list<int> from_array(arr, arr + 4);
// 从vector初始化
std::vector<int> vec = {5, 6, 7, 8, 9};
std::list<int> from_vector(vec.begin(), vec.end());
4.3 复杂数据类型初始化
class Person {
public:
std::string name;
int age;
Person(std::string n, int a) : name(n), age(a) {}
};
std::list<Person> people = {
Person("Alice", 25),
Person("Bob", 30),
Person("Charlie", 35)
};
4.4 使用 assign() 重新初始化
std::list<int> mylist;
mylist.assign(5, 10); // {10, 10, 10, 10, 10}
mylist.assign({1, 2, 3, 4, 5}); // {1, 2, 3, 4, 5}
std::vector<int> vec = {6, 7, 8};
mylist.assign(vec.begin(), vec.end()); // {6, 7, 8}
五、最佳实践总结
5.1 填充操作
- 首选:
std::fill()用于范围填充 - 备选:
std::fill_n()用于指定数量填充 - 避免:手动循环,除非有特殊需求
5.2 迭代器导航
- 通用方案:
std::next()和std::prev() - 随机访问容器:可以使用
+运算符 - 重要原则:始终进行边界检查
5.3 容器初始化
- 简单情况:使用初始化列表
{} - 重复值:使用
(size, value)构造函数 - 复杂场景:使用迭代器范围或
assign()方法
5.4 通用建议
- 优先使用标准库算法,它们经过优化且更安全
- 理解迭代器类别,选择合适的方法
- 始终考虑边界情况,避免未定义行为
- 保持代码一致性,在团队中使用统一的编码风格
结语
stl的方法很多,记住几个常用的就行,随用随查。