我应该在我的代码中使用std::vector::at()吗?

16
我今天注意到std::vector::at()访问值要比使用方括号[]慢得多。根据文档,.at()更安全,因为它不允许我访问数组范围之外的值。然而,即使我使用at()访问越界的值,我仍然会遇到错误,所以无论如何我都需要避免这种情况。
那么是否有什么好的理由让任何人使用at()而不是[]呢?

1
你是否开启了编译优化? - Rafał Rawicki
不确定,我正在使用默认构建选项的QtCreator,并且在文档中找不到任何“优化”标志。 - laurent
1
可能是使用DEBUG编译的原因导致std::vector::at()明显变慢。 - Rafał Rawicki
2
@Rafal:说得好 - 在没有异常的情况下,异常代码不应该对代码流程产生太大的干扰 - 你主要需要支付一个额外的检查和一点调用站点的代码膨胀(可能会影响缓存性能),但是通过优化,异常代码的影响应该是相当小的。 - Kerrek SB
6个回答

23

如果你认为索引不在你的控制之内,或者控制流程特别复杂且你要跟踪错误,那么你可能想在调试阶段使用 at(),但永远不要在循环内或任何你知道索引是安全的情况下使用。

即使在其他情况下,你也应该预验证索引(例如,如果它是用户输入),或者如果你只是从一个复杂的算法中获取值,则使用 assert 并修复其中的错误。[编辑] 或者,如果你正在编写非常复杂的算法,并且不确定所有索引是否始终有效,那么你可以在该算法内部使用 at(),并将调用放入 try 块中——但即使在这种情况下,最好还是采用断言方式。

个人而言,我看不出任何 at() 能够在发布代码中生存的好理由。你可能可以构造一些例子,想要使用异常处理作为方便的方式来指导你的控制流程,但是这样的用例都非常具体。


+1 对于 Kerrek 的解释比一个有边界检查,一个没有边界检查的更好。 - RoundPi
1
此外,在调试模式下,一些实现在使用 operator[] 时也会执行边界检查,例如 VC++(可能还包括 Dinkumware 库)。 - Praetorian
6
我不是点赞负责人(也认为答案不应该被点踩),但考虑到[]唯一的优势是速度,如果在你编写的函数中速度并不重要,我更倾向于使用at()。我们的代码设置了记录异常抛出的模块,因此使用at()可以更轻松/快速地解决我们或QA在全新的模块和功能中发现的傻瓜式错误。 - Warpin

12

at()operator[] 的区别在于,at() 在请求的位置超出范围时通过抛出 out_of_range 异常来进行信号传递。
因此,通过使用 at() 您可以对错误状态做出反应。如果使用 operator[] 访问向量超出索引,则会导致未定义行为。


operator[] 很可能会从向量保留的内存中返回未定义的值。 - Viktor Sehr
3
正确的说法是“访问越界索引会导致未定义的行为”。 - Kerrek SB
1
我澄清了我的帖子。但是这个应用程序很可能会崩溃。 - mkaes

4

at进行范围检查,但operator[]不会。例如,如果你将-1传递给at(),则会抛出一个std::out_of_range异常。但是,如果你对operator[]做同样的事情,它会崩溃或产生奇怪的结果。

如果你绝对确定索引没有问题或者想自己进行检查,请使用operator[]


1

at() 返回索引为 i 的元素,如果索引 i 超出范围,则抛出范围错误异常。因此,我建议使用 at() 而不是 [],因为后者会产生未定义的行为,如果超出范围。如果您想在程序中保证安全,请使用 at() :)


1

at() 会抛出 out_of_range 异常,而 [] 不会。

因此,如果您尝试访问超出范围的内容,[] 可能会立即使应用程序崩溃,而 at() 则可以在运行时处理错误。

如果这对您很重要(通常情况下不需要,因为访问超出范围的内容自动意味着代码语义不按预期工作),则应使用 at()。


0
假设您没有将异常用作某种信号系统,那么区别在于vector::at()将始终在尝试访问越界索引时抛出异常。vector::operator[]可能会返回未定义的值或抛出访问冲突异常(或崩溃)。

据说 operator[] 还可能会格式化您的硬盘或订购披萨。列出由 UB 引起的可能事件的包容性清单,最好是不可能的,最坏的情况是具有误导性的... - Kerrek SB

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接