优先用 at() 读取已知存在的键或 const 容器,因它不插入默认值且支持 const;用 [] 仅当需插入/修改;不确定存在性时改用 find() 或 contains()。
![c++ map中下标[]和at()怎么区分使用](https://img.php.cn/upload/article/001/221/864/179074273798955.png)
用 [] 还是 at()?先看你要不要插入新键
核心判断就一条:[] 会在键不存在时**自动插入默认构造的值**(比如 int 变成 0,std::string 变成空串),而 at() 遇到不存在的键直接抛 std::out_of_range 异常,不插、不改、不妥协。
所以如果你写 m["missing"],哪怕 m 原来是空的,这行代码执行完后 m.size() 就变成 1 了——很多人踩坑就是这儿:查个数,结果把数据污染了。
- 想插入或修改值 → 用
[] - 只想读取已知存在的键 → 用
at() - 不确定键是否存在,又不想意外插入 → 别用
[],改用find()或 C++20 的contains()
at() 能在 const map 上用,[] 不行
这是编译期就能卡住的硬限制。如果你有个 const std::map<:string int>& m</:string>,下面这行会编译失败:
m["key"] = 42; // error: operator[] is not const-qualified
但这个可以:
立即学习“C++免费学习笔记(深入)”;
组合式C++代码评审方案,融合静态分析、AI推理、多轮迭代评审和C++专项检查,适用于PR审查、增量代码审查、全项目评审和代码质量评分,触发词包括review cpp、cpp代码评审、C++review、代码审查。
int val = m.at("key"); // OK: at() has const overload
所以当你写函数参数是 const 引用、或者处理只读配置时,at() 是唯一安全的访问方式;强行用 [] 会触发编译错误,不是运行时问题。
异常处理成本低,但得主动接住
at() 抛异常不是缺陷,是设计意图——它把“键不存在”这件事从隐式行为(默默插个 0)变成显式信号。实际用的时候别裸调 at():
- 确定键一定存在(比如刚插入完立刻读)→ 直接
at(),省去检查 - 不确定,但业务上“不存在”算错误 → 用
try/catch捕获std::out_of_range,做降级或报错 - 不确定,且“不存在”是正常分支 → 别用
at(),改用find()或contains()判断再处理
注意:at() 的异常开销在现代编译器下几乎可忽略(无栈展开优化后),关键是语义清晰,不是性能瓶颈。
unordered_map 也完全适用这套规则
std::unordered_map 和 std::map 在 [] 与 at() 的行为上**完全一致**:插入逻辑、异常类型、const 支持、返回类型都一样。区别只在底层实现(红黑树 vs 哈希表)和迭代顺序(有序 vs 无序),不影响这两个访问方式的选择逻辑。
真正容易被忽略的是:很多人以为 at() 比 [] 慢,其实两者平均时间复杂度都是 O(log n)(map)或 O(1)(unordered_map),差异在常数项,不在算法层级。选哪个,只取决于你想要语义,而不是“快一点”。

















