vector :: at vs. vector :: operator []

时间:2012-02-21 10:38:01

标签: c++ stl stdvector

我知道at()由于其边界检查而慢于[],这也在C++ Vector at/[] operator speed::std::vector::at() vs operator[] << surprising results!! 5 to 10 times slower/faster!等类似问题中进行了讨论。我只是不明白at()方法有什么用处。

如果我有一个像这样的简单向量:std::vector<int> v(10);我决定在我有索引{{1}的情况下使用at()代替[]来访问其元素我不确定它是否在向量范围内,它迫使我用try-catch块包装它

i

虽然我能够通过使用try { v.at(i) = 2; } catch (std::out_of_range& oor) { ... } 和我自己检查索引来获得相同的行为,这对我来说似乎更容易和方便:

size()

所以我的问题是:
使用vector::at优于vector::operator[]有什么好处?
我应该何时使用vector::at而不是vector::size + vector::operator[]

8 个答案:

答案 0 :(得分:58)

我会说vector::at()抛出的异常并不是真正意图被周围的代码捕获。它们主要用于捕获代码中的错误。如果需要在运行时进行边界检查,例如索引来自用户输入,你最好使用if语句。总而言之,设计代码的目的是vector::at()永远不会抛出异常,这样如果确实如此,并且程序中止,则表明存在错误。 (就像assert()

答案 1 :(得分:16)

  

它迫使我用try-catch块包装它

不可以(try / catch块可以在上游)。当您希望抛出异常而不是程序进入未定义的行为领域时,它非常有用。

我同意大多数对边界的访问都是程序员的错误(在这种情况下你应该使用assert来更容易地找到这些错误;标准库的大多数调试版本会自动为你执行此操作)。您不希望使用可以上游吞下的异常来报告程序员错误:您希望能够修复错误

因为对于向量的越界访问不太可能是正常程序流的一部分(在它是的情况下,你是对的:事先用size检查而不是让异常冒泡),我同意你的诊断:at基本没用。

答案 2 :(得分:11)

  

使用vector :: at over vector :: operator []有什么好处?   我什么时候应该使用vector :: at而不是vector :: size + vector :: operator []?

这里的重点是异常允许将正常的代码流与错误处理逻辑分开,并且单个catch块可以处理从无数抛出站点生成的问题,即使它们分散在函数调用的深处。因此,并非at()对于单次使用而言必然更容易,但是当您需要大量索引进行验证时,有时它会变得更容易 - 并且不会混淆正常情况逻辑。

值得注意的是,在某些类型的代码中,索引以复杂的方式递增,并且不断用于查找数组。在这种情况下,使用at()确保正确检查要容易得多。

作为一个真实世界的例子,我有代码将C ++标记为词法元素,然后是将标记移动到标记向量的其他代码。根据遇到的情况,我可能希望增加并检查下一个元素,如:

if (token.at(i) == Token::Keyword_Enum)
{
    ASSERT_EQ(tokens.at(++i), Token::Idn);
    if (tokens.at(++i) == Left_Brace)
        ...
    or whatever

在这种情况下,很难检查你是否不恰当到达输入的末尾,因为这非常依赖于遇到的确切令牌。在每个使用点进行明确检查是痛苦的,并且程序员错误的余地更多,因为前/后增量,使用点的偏移,关于某些早期测试的持续有效性的错误推理等等。

答案 3 :(得分:5)

首先,未指定at()operator[]是否较慢。什么时候 没有界限错误,我希望它们的速度大致相同 至少在调试版本中。不同之处在于at()指定 究竟会发生什么,有一个边界错误(例外), 在operator[]的情况下,它是未定义的行为-a 在我使用的所有系统(g ++和VC ++)中崩溃,至少在什么时候崩溃 使用正常的调试标志。 (另一个区别是,一旦我 确定我的代码,operator[]可以大幅提高速度 通过关闭调试。如果表演需要它 - 我 除非有必要,否则不会这样做。)

在实践中,at()很少适用。如果上下文是这样的话 你知道索引可能无效,你可能想要显式测试 (例如,返回默认值或其他内容),如果您知道它 不能无效,你想中止(如果你不知道是否中止 可能无效或无效,我建议你指定你的功能 界面更精确)。但是,有一些例外,其中 解析用户数据和错误可能导致无效索引 应该导致整个请求的中止(但不会带来服务器 下);在这种情况下,例外是恰当的,at()会这样做 适合你。

答案 4 :(得分:4)

如果你有一个指向vector的指针,那么

at会更清楚:

return pVector->at(n);
return (*pVector)[n];
return pVector->operator[](n);

除了性能之外,第一个是更简单,更清晰的代码。

答案 5 :(得分:1)

使用异常的全部意义在于您的错误处理代码可以更远。

在这种特定情况下,用户输入确实是一个很好的例子。想象一下,您希望在语义上分析XML数据结构,该结构使用索引来引用您在std::vector内部存储的某种资源。现在XML树是一棵树,所以你可能想用递归来分析它。在内部深处,在递归中,XML文件的编写者可能存在访问冲突。在这种情况下,您通常希望突破所有级别的递归,只是拒绝整个文件(或任何类型的“粗糙”结构)。这就是派上用场的地方。如果文件有效,您可以编写分析代码。库代码将负责错误检测,您可以在粗略级别上捕获错误。

此外,其他容器(如std::map)也有std::map::at,其语义与std::map::operator[]略有不同:at可以在const地图上使用,而operator[]则不能。现在,如果您想编写与容器无关的代码,例如可以处理const std::vector<T>&const std::map<std::size_t, T>&的内容,ContainerType::at将是您的首选武器。

但是,所有这些情况通常在处理某种未经验证的数据输入时出现。如果您确定有效范围(通常应该是这样),通常可以使用operator[],但更好的是使用begin()end()的迭代器。

答案 6 :(得分:0)

根据this文章,除了性能之外,使用atoperator[]并没有任何区别,只要保证访问权限在向量的范围内。否则,如果访问仅基于向量的容量,则使用at更安全。

答案 7 :(得分:0)

注意:似乎有些新人在没有礼貌地说出错误的情况下正在摒弃这个答案。以下答案是正确的,可以通过here进行验证。

实际上只有一个区别:at确实限制检查而operator[]没有。这适用于调试版本和发布版本,这是标准规定的。就这么简单。

这使at方法变慢,但不使用at也是一个非常糟糕的建议。你必须看绝对数字,而不是相对数字。我可以肯定地说,你的大多数代码都比at做更昂贵的操作。就个人而言,我尝试使用at因为我不想要一个讨厌的bug来创建未定义的行为并潜入生产。