C++——unordered_map学习

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. 迭代器的内部构造:firstsecond

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 循环的底层并不是“修改同一个变量”,而是**“每一轮都重新创建”**。

编译器视角下的底层逻辑:

  1. 编译器维护一个隐藏的、可变的迭代器(比如 __it)来控制进度的移动。
  2. 在每一轮循环开始时,创建一个全新的局部变量 i(或者 [name, score]),并用当前迭代器指向的内容对其进行初始化。
  3. 在这一轮循环结束时,局部变量 i 被销毁。
  4. 隐藏的迭代器自增(++__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 或大的结构体)时,我们强调一定要加 & 的原因。