unordered_map 是C++中的哈希表容器,表中未排序。
1. find()函数
find() 是 unordered_map 最常用的成员函数。
- 返回值类型:返回一个迭代器(Iterator)。
- 返回值逻辑:
- 找到目标:返回指向该键值对(
std::pair)的迭代器。 - 未找到:返回
my_map.end()(指向容器末尾之后的标记,不可解引用)。
- 找到目标:返回指向该键值对(
- 安全实践:在使用返回值前,必须判断是否等于
end()。
C++
auto it = my_map.find(key);
if (it != my_map.end()) {
// 安全操作
}2. 迭代器的内部构造:first 与 second
unordered_map 存储的元素本质上是 std::pair<const Key, Value>。
it->first:访问 Key(键)。它是const的,无法修改(修改 Key 会破坏哈希表结构)。it->second:访问 Value(值)。只要不是const_iterator,就可以读写。
3. 现代 C++ 遍历利器:范围 for 循环与结构化绑定
C++17 引入了结构化绑定(Structured Bindings),让遍历代码告别了生硬的 first/second。
C++
for (const auto& [name, score] : scores) {
std::cout << name << ": " << score << std::endl;
}| 修饰符 | 作用 | 不加会怎样? |
const | 安全性:声明只读权限,防止意外修改。 | 失去编译器保护,可能导致数据被误改。 |
& (引用) | 性能:直接访问原件,避免数据拷贝。 | 每一轮循环都会发生数据复印(拷贝),处理大数据时性能剧降。 |
4. const 变量如何实现“动态”遍历?
一个直觉误区是:“如果变量 i 是 const 的,它怎么变到下一个元素去?”
范围 for 循环的底层并不是“修改同一个变量”,而是**“每一轮都重新创建”**。
编译器视角下的底层逻辑:
- 编译器维护一个隐藏的、可变的迭代器(比如
__it)来控制进度的移动。 - 在每一轮循环开始时,创建一个全新的局部变量
i(或者[name, score]),并用当前迭代器指向的内容对其进行初始化。 - 在这一轮循环结束时,局部变量
i被销毁。 - 隐藏的迭代器自增(
++__it),进入下一轮。
const 锁住的是单次循环内你对数据的操作权限,而控制循环“动起来”的逻辑是由编译器在底层管理的,两者互不干扰。
C++
for (const int i : v) {
std::cout << i << " ";
// i++; // 报错:i 是只读的
}在 for (const int i : v) 的运行过程中,i 的值在每一轮循环中都是不同的,但它在单次循环内部是不可变的。
编译器在处理这一行代码时,实际上把它转化成了一个类似这样的逻辑:
C++
// 编译器生成的伪代码
auto __it = v.begin();
auto __end = v.end();
for (; __it != __end; ++__it) {
const int i = *__it; // <--- 关键点在这里!
std::cout << i << " ";
// 你的代码逻辑...
}这里的核心在于:i 并不是在整个循环过程中共用同一个变量。
- 如果你写
for (const int i : v):每一轮都在做i = *__it(拷贝赋值)。 - 如果你写
for (const auto& [k, v] : m):每一轮都在做类似auto& i = *__it(建立引用/别名)。
引用就像是给原件贴了个标签,而不是复印一份。这就是为什么处理复杂对象(如 std::string 或大的结构体)时,我们强调一定要加 & 的原因。