vector::at() 在越界时抛 std::out_of_range 异常,operator[] 不检查越界、行为未定义;debug 模式下部分实现可能增强检查但不可依赖;at() 有异常开销但通常可忽略,应先 profile 再优化。

vector::at() 会抛异常,operator[] 不检查越界
vector::at() 在索引超出 size() 范围时抛出 std::out_of_range 异常;而 operator[] 完全不检查,行为是未定义的(UB)。这意味着用 operator[] 访问 v[100] 时,即使 v.size() == 5,编译器不会报错、运行时也不一定崩溃——可能读到垃圾值,也可能意外覆盖相邻内存,调试极难定位。
debug 和 release 模式下 operator[] 表现可能不同
某些标准库实现(如 libstdc++ 的 debug mode 或 MSVC 的 _ITERATOR_DEBUG_LEVEL=2)会在 debug 下为 operator[] 加额外检查,但这是非标准行为,不可依赖。release 模式下一律无检查。所以不能靠“本地跑通”判断越界是否安全。
- 用
at()是显式表达“此处需边界保障”,意图清晰 - 用
operator[]是显式承担越界风险,常见于性能敏感且逻辑已确保安全的场景(如循环内反复访问,且索引由size()控制) - 静态分析工具(如 clang-tidy)能对
at()做更准的流敏感检查,对operator[]几乎无能为力
at() 的开销不只是“多一次比较”
除了多一次 if (i >= size()) 判断,at() 还隐含异常处理机制的准备成本:编译器可能禁用部分优化,栈展开信息需保留。在 tight loop 中频繁调用 at() 确实有可观开销。但多数业务代码里,这点开销远小于一次缓存未命中或分支预测失败。
- 别盲目替换成
operator[],先 profile 确认它真是瓶颈 - 若确定安全且 hot path,可用
data()[i]替代operator[](更裸,但语义等价) - 注意:即使用了
data(),仍需自己保证i ,否则仍是 UB
自定义容器或封装 vector 时容易忽略的细节
很多人封装 vector 写个 get(i) 方法,却忘了转发 at() 的异常规格说明(noexcept 与否),或误把 operator[] 当作“默认安全”接口暴露出去。结果上层调用者以为不会抛异常,实际却因越界触发 UB。
立即学习“C++免费学习笔记(深入)”;
- 若封装函数语义等价于
at(),就该声明throw(std::out_of_range)或 C++11 后用noexcept(false) - 若封装函数内部做了范围断言(如
assert(i ),那它只在 debug 生效,release 下失效——这和operator[]本质一样,不是“安全替代” - 第三方库(如 Eigen、xtensor)往往提供
operator()带检查、unchecked()供性能场景,这种分离更明确
at() 不代表懒,用 operator[] 也不代表快——关键在上下文里是否真能排除越界可能。










