我知道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[]?
答案 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)
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文章,除了性能之外,使用at
或operator[]
并没有任何区别,只要保证访问权限在向量的范围内。否则,如果访问仅基于向量的容量,则使用at
更安全。
答案 7 :(得分:0)
注意:似乎有些新人在没有礼貌地说出错误的情况下正在摒弃这个答案。以下答案是正确的,可以通过here进行验证。
实际上只有一个区别:at
确实限制检查而operator[]
没有。这适用于调试版本和发布版本,这是标准规定的。就这么简单。
这使at
方法变慢,但不使用at
也是一个非常糟糕的建议。你必须看绝对数字,而不是相对数字。我可以肯定地说,你的大多数代码都比at
做更昂贵的操作。就个人而言,我尝试使用at
因为我不想要一个讨厌的bug来创建未定义的行为并潜入生产。