22

C++ 的 container vector, deque, ...at(index)除了operator[index]访问容器元素外,还提供访问器功能。

此成员函数和成员运算符函数 operator[] 之间的区别在于,如果请求的位置超出范围,则 deque::at 会通过抛出 out_of_range 异常来发出信号。

我从来没有在我的代码中需要这个函数,因为在我的 C++ 代码中访问可能超出范围的元素是没有意义的。始终编​​写代码以访问正确的索引(或在索引无法匹配的情况下产生有意义的错误/异常。)

at()我会对生产代码中使用的真实世界示例(可能来自一些开源项目,因为这会添加一些上下文)感兴趣。

也许有人可以举一个使用at()有意义的算法问题的例子。

注意:我最近在一些单元测试代码中使用了它,其中添加索引检查代码被认为不值得麻烦,并且抛出的 out_of_range 异常被at()认为是足够的 info+context 以防测试中断。

注意:关于ildjarn 的回答- 我不想就此展开讨论或评论。我对“积极”的发现很感兴趣,这是使用它具体例子。谢谢你。

4

8 回答 8

13

好吧,当您不控制正在使用的索引时(例如,如果它是由您的代码的客户端传入的),您应该手动检查它是否在范围内,或者用于at获取报告的异常(您可以捕获并使用您自己的错误报告通知调用者,或者只是向上传播标准异常)。

换句话说,检查输入参数是被调用函数的责任,但是它是通过if语句显式执行此操作还是通过使用at而不是隐式执行此操作[]是一个有争议的问题。如果我要做的只是抛出一个out_of_range异常(如果传入的索引大于或等于集合的大小),我想我会放手去做at并为自己节省一些编码。

默默地传回坏数据几乎从来都不是最好的解决方案。简单地将 x[7] 传回四元素整数甲板的问题在于调用者认为它是一个有效的零。事实并非如此。

于 2011-04-13T07:26:27.913 回答
7

在我看来,at()是一个100% 没用的成员函数。仅在标准库容器的有效范围内访问是使用该容器的先决条件,任何先决条件的违反都应使用 anassert而不是抛出异常来处理。的存在at()绝不有助于容器维护其先决条件/不变量,实际上只会通过使适当的边界检查访问看起来不是先决条件来混淆问题

即,为最终只能由程序员错误引起的事情抛出异常是非常愚蠢的。有关更详细的解释,请参阅此线程,特别是 D. Abrahams 的帖子;虽然可能很长,但绝对值得一读:comp.lang.c++.moderated: Exceptions

编辑:为了澄清对 OP 的补充说明,我是说根据我对 C++ 的经验——专业、开源和其他方面——我从未遇到过使用标准容器的情况at(),并保持这一点它实际上并没有在生产代码中使用。进一步的评论或详细说明只是为了合理化我认为是这种情况的原因。

于 2011-04-13T07:22:01.410 回答
6

我一直发现at()有用的一个用例是促进复杂用户输入的解析。例如,在分析 C++ 代码时,我发现自己在检查语法结构时沿着一系列词汇标记移动。逻辑通常类似于“如果这个标记是一个标识符,而下一个是一个 Equals,那么它应该是一个赋值,所以提前扫描一个分号标记以建立表达式的标记范围”。在这样的代码中使用at()意味着您可以轻松地在与当前点的某个偏移处表达期望,ala:

if (tokens.at(i) == Switch)
{
    if (tokens.at(++i) != Left_Parentheses)
        // throw or return to say input program's broken...
    if (tokens.at(++i) == ...)
        ...
}

每当您尝试解析无效程序时,都会出现异常。增加位置发生在整个代码中的许多地方,以至于不断地重新验证大小将是一场噩梦(冗长且极易出错),因为在这种情况下,您只会意识到程序需要多大才能有效,因为应用语法规则。与功能等效的替代方案相比,此处使用at()简洁、健壮、直观且性能合理。

FWIW - 快速搜索我们的生产代码(200k 行,大部分是在我加入团队之前编写的)发现了at().

于 2011-04-15T02:23:52.233 回答
5

我的情况宁愿是:为什么不使用它

除非您处于应用程序的性能关键部分,否则您应该始终std::out_of_range反对Undefined Behavior,至少这是我的信条。

在实践中,我通常将我正在处理的所有代码转换为使用检查访问。对于大多数代码来说,性能损失是不可见的,至少我有一个很好的报告,其中包含有关当前执行上下文(在catch(std::exception const&)根级别生成)的信息,而不是导致我的代码有时失败的内存损坏稍后(或更糟糕的是,看起来它起作用了)。

我同意首先应该验证输入,我同意您应该事先检查您的访问权限...但以防万一您忘记或遇到错误,最好有一个at().

使用[]而不是at()就像在口袋里没有/有(分别)安全性的情况下携带上膛的枪一样。你可以忘记戴上它,但心甘情愿地取下它?那是精神错乱。

于 2011-04-13T09:42:30.123 回答
4

经过快速搜索,我发现 Inkscape(svg 编辑器)、Google v8、Android、Chromium 和 Ogre 等都使用了此功能。这个(基本的)列表取自一个简单的谷歌搜索,使用正则表达式at\([0-9]+\)

使用\.at\([0-9a-z_]+\)而不是前面的表达式给出了更通用的结果,并添加了OpenJdk和丰富的 sourceforge 项目。

于 2011-04-13T10:55:03.777 回答
0

v.at(-1) won't fail like v[-1] (you'll get an exception)

于 2011-04-13T10:26:03.740 回答
0

我同意这里很多人的观点,这些人at大多是无用的;但是,在使用指向(或列表)容器的指针时,它可能看起来更好:

std::vector<std::vector<int> >::iterator i;
for (i = v2.begin(); i != v2.end(); ++i)
{
    int x1 = (*i)[3]; // UGLY
    int x2 = i->at(3); // Looks OK
}

我认为这段代码在使用时看起来更好at

于 2011-04-13T16:53:02.910 回答
0

Stroustrup 建议at在所有地方都使用 ,除非您确定索引将在有效范围内。

他建议对于像下面这样的代码可以使用[]操作符。

for (int i = 0; i < v.size(); ++i)
{
    // use v[i] - we are sure it will be a valid index
}

在其他情况下,使用at

于 2012-11-22T02:40:40.927 回答