C++的容器vector
、deque
。。。除了CCD_ 4之外,还提供CCD_。
此成员之间的差异函数和成员运算符函数运算符[]是deque::at信号如果请求的位置超出投掷界外球的射程例外
我从来没有在我的代码中需要过这个函数,因为在我的C++代码中访问可能超出范围的元素从来都没有意义。编写代码总是为了访问正确的索引(或者在索引无法匹配的情况下产生有意义的错误/异常。(
我会对在生产代码中使用at()
的真实世界示例感兴趣(可能来自某个开源项目,因为这会添加一些上下文(。
也许有人可以举一个使用at()
有意义的算法问题的例子。
注意:我最近在一些单元测试代码中使用了它,添加索引检查代码被认为不值得麻烦,at()
引发的out_of_range异常被认为是足够的信息+上下文,以防测试中断
注意:关于ildjarn的这个回答,我不想就此展开讨论或评论战。我对"积极"的发现很感兴趣,也就是使用的具体例子。非常感谢。
好吧,当你不能控制正在使用的索引时(例如,如果它是由代码的客户端传入的(,你应该手动检查它是否在范围内,或者使用at
报告异常(你可以用自己的错误报告捕获并通知调用方,或者简单地向上传播标准异常(。
换句话说,检查输入参数是被调用函数的责任,但它是用if
语句显式地检查输入参数,还是用at
而不是[]
隐式地检查,这是一个有争议的问题。如果我要做的只是抛出一个out_of_range
异常(如果传入的索引大于或等于集合的大小(,我想我会让at
来做,并为自己节省一些代码。
默默地传递坏数据几乎从来都不是最好的解决方案。简单地为四元素整数组传递x[7]的问题在于,调用者认为它是一个有效的零。事实并非如此。
在我看来,at()
是一个100%无用的成员函数。仅在标准库容器的有效范围内访问是使用该容器的先决条件,违反任何先条件的行为都应使用assert
处理,而不是抛出异常。at()
的存在决不能帮助容器维护其前提条件/不变量,事实上,它只是通过使正确的边界检查访问看起来不是的前提条件来混淆问题。
也就是说,为一些最终只能由程序员错误引起的事情抛出异常是非常愚蠢的。请参阅此线程以获得更详细的解释,特别是D.Abrahams的帖子;尽管它可能很长,但绝对值得一读:comp.lang.c++.mediated:Exceptions。
编辑:为了回应OP的补充说明,我想说的是,在我使用C++的经验中——专业的、开源的或其他的——我从未遇到过使用标准容器的at()
,并坚持认为它实际上在生产代码中没有使用。进一步的评论或阐述只是为了合理解释为什么我认为是这样。
我一直认为at()
在中的一个用例很有用,它有助于解析复杂的用户输入。例如,在分析C++代码时,我发现自己在检查语法结构时会沿着一组词汇标记移动。逻辑通常类似于"如果这个令牌是一个标识符,而下一个是一个Equals,那么它应该是一个赋值,所以提前扫描分号令牌以建立表达式的令牌范围"。在这样的代码中使用at()
意味着您可以很容易地在与当前点的某个偏移处表达期望,例如:
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——快速搜索我们的生产代码(20万行,大部分是在我加入团队之前写的(发现了at()
的十几种用法。
我的情况是:为什么不使用它?
除非您处于应用程序的性能关键部分,否则您应该始终支持std::out_of_range
而不是未定义的行为,至少这是我的信条。
在实践中,我通常会将正在处理的所有代码转换为使用检查的访问。对于大多数代码来说,性能损失是看不见的,至少我得到了一份很好的报告,其中包含了当前执行上下文的信息(在根级别的catch(std::exception const&)
中生成(,而不是内存损坏,这会使我的代码在某些时候失败(或者更糟的是,看起来很有效(。
我同意首先应该验证输入,我同意您应该事先检查您的访问权限。。。但万一你忘记了或有错误,最好有一个at()
。
使用[]
而不是at()
就像携带一把上了膛的枪,口袋里没有/也没有(相应的(安全装置。你可以忘记戴上它,却心甘情愿地把它取下来?这太疯狂了。
经过快速搜索,我发现Inkscape(svg编辑器(、Google v8、Android、Chromium和Ogre等都使用了此功能。这个(基本的(列表取自一个简单的谷歌搜索,使用正则表达式at([0-9]+)
。
使用.at([0-9a-z_]+)
代替前面的表达式可以得到更通用的结果,并添加了OpenJdk和大量的sourceforge项目。
v.at(-1)
不会像at(index)
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
时,此代码看起来更好。
Stroustrup建议在所有地方使用at
,除非您确信索引在有效范围内。
他建议,对于下面这样的代码,可以使用[]
运算符。
for (int i = 0; i < v.size(); ++i)
{
// use v[i] - we are sure it will be a valid index
}
在其他情况下,使用at