vector :: at vs vector :: operator
我知道at()
慢于[]
是因为它的边界检查,这也是类似的问题,如C ++向量在/ []运算符速度或:: std :: vector :: at()vs运算符[] < <令人惊讶的结果! 慢5到10倍/更快! 。 我只是不明白at()
方法的好处。
如果我有这样一个简单的向量: std::vector<int> v(10);
我决定通过使用at()
而不是[]
来访问它的元素,当我有一个索引i
,我不确定它是否在向量边界,它迫使我用try-catch块包装它 :
try { v.at(i) = 2; } catch (std::out_of_range& oor) { ... }
尽pipe我可以通过使用size()
来获得相同的行为,并且自己检查索引,这对我来说似乎更容易和方便:
if (i < v.size()) v[i] = 2;
所以我的问题是:
使用vector :: at over vector :: operator []的优点是什么?
什么时候应该使用vector :: at而不是vector :: size + vector :: operator [] ?
我会说, vector::at()
抛出的exception并不是真的打算被周围的代码所捕获。 它们主要用于捕获代码中的错误。 如果你需要在运行时进行界限检查,因为例如索引来自用户input,你最好使用if
语句。 所以总的来说,devise你的代码的目的是vector::at()
永远不会抛出一个exception,所以如果这样做,你的程序中止,这是一个错误的迹象。 (就像一个assert()
)
它迫使我用try-catch块来包装它
不,它不(try / catch块可以在上游)。 当你想抛出一个exception,而不是你的程序进入未定义的行为领域时,这是有用的。
我同意大多数向量的访问是一个程序员的错误(在这种情况下,你应该使用assert
更容易地find这些错误;大多数标准库的debugging版本自动为你做这个)。 你不想使用上游的exception来报告程序员的错误:你想能够修复这个bug 。
由于对向量的越界访问不太可能是正常程序stream的一部分(在这种情况下,你是对的:事先检查size
而不是让exception冒出来),我同意你的诊断: at
本质at
是无用的。
使用vector :: at over vector :: operator []的优点是什么? 什么时候应该使用vector :: at而不是vector :: size + vector :: operator []?
这里重要的一点是,exception允许从error handling逻辑中分离正常的代码stream,并且单个catch块可以处理从无数个抛出站点产生的问题,即使是在函数调用中分散深处也是如此。 所以,并不是说at()
对于单次使用来说一定比较容易,但是当有很多索引要validation的时候,它有时会变得更容易,而且对正常情况逻辑的混淆也更less。
值得注意的是,在某些types的代码中,索引正在以复杂的方式递增,并不断用来查找数组。 在这种情况下,使用at()
更容易确保正确的检查。
作为一个真实世界的例子,我有代码将C ++标记为词法元素,然后是其他代码将索引移动到标记的向量上。 根据遇到的情况,我可能希望增加和检查下一个元素,如下所示:
if (token.at(i) == Token::Keyword_Enum) { ASSERT_EQ(tokens.at(++i), Token::Idn); if (tokens.at(++i) == Left_Brace) ... or whatever
在这种情况下,很难检查是否不恰当地到达input的末尾,因为这非常依赖于遇到的确切的标记。 在每个使用点进行显式检查都是很痛苦的,程序员错误的前后增量,使用时的偏移量,以及某些早期testing的持续有效性的错误推理等,
首先,没有指定at()
或operator[]
是否较慢。 当没有界限的错误,我希望他们是相同的速度,至less在debugging构build。 不同之处在于at()
指定了会发生什么情况,会出现一个界限错误(一个exception),在operator[]
的情况下,它是未定义的行为 – 在我使用的所有系统(g ++和VC ++),至less在使用正常的debugging标志时。 (另一个区别是,一旦我确定了我的代码,就可以通过closuresdebugging程序来大大提高operator[]
速度。如果性能要求的话,除非有必要,否则我不会这样做)。
在实践中, at()
很less适合。 如果上下文是这样的,你知道索引可能是无效的,你可能想要明确的testing(例如返回一个默认值或什么的),如果你知道它不能是无效的,你想中止(如果你不知道它是否是无效的,我build议你更精确地指定你的函数的接口)。 但是,有一些例外情况,无效索引可能是由parsing用户数据导致的,并且错误应该导致整个请求中止(但不会导致服务器closures)。 在这种情况下,一个例外是适当的,并at()
将这样做给你。
如果你有一个指向vector的指针,可以更清楚:
return pVector->at(n); return (*pVector)[n]; return pVector->operator[](n);
除了性能,其中第一个是更简单和更清晰的代码。
使用exception的重点在于您的error handling代码可能更远。
在这个特定的情况下,用户input确实是一个很好的例子。 想象一下,你想要在语义上分析一个XML数据结构,它使用索引来引用你内部存储在std::vector
中的某种资源。 现在XML树是一棵树,所以你可能想用recursion来分析它。 在recursion的深处,XML文件的作者可能会存在访问冲突。 在这种情况下,你通常想从所有的recursion级别中跳出来,只是拒绝整个文件(或任何一种“粗糙的”结构)。 这是在哪里派上用场。 您可以将分析代码编写为 – 如果文件有效。 库代码将负责错误检测,您可以在粗略级别捕获错误。
另外,像std::map
这样的其他容器也有std::map::at
,它与std::map::operator[]
有稍微不同的语义:at可以在const map上使用,而operator[]
不能。 现在,如果你想编写容器不可知的代码,就像可以处理const std::vector<T>&
或const std::map<std::size_t, T>&
, ContainerType::at
是你的武器select。
但是,处理某种未经validation的数据input时通常会出现所有这些情况。 如果您确定自己的有效范围,那么您通常可以使用operator[]
,但更好的方法是使用begin()
和end()
。
根据这篇文章,除了性能之外,使用at
或者operator[]
并没有什么区别,只要访问权限保证在vector的大小范围内。 否则,如果访问只是基于vector的容量,则使用更安全。