请告诉我,std::deque 有一个“.at”方法——它提供对元素的访问。
“at”和迭代器访问之间有区别吗?
这是一个例子:
std::deque<int>::iterator deq_iterator = my_deq.begin();
for (int i = 0; i < my_deq.size(); i++)
{
std::cout << *deq_iterator << std::endl;
deq_iterator++;
}
...
for (int i = 0; i < my_deq.size(); i++)
{
std::cout << my_deq.at(i) << std::endl;
}
然后在第一种情况下,遍历元素的循环将是高效的,也就是说,要访问下一个 i 元素,它将从前一个迭代器开始,每次循环迭代都会增加一个,而不是每次都从队列的零元素开始。
但是在使用“at”方法时是否也会这样做,或者在这种情况下,每次访问第 i 个洗脱液时都会从队列的零元素中寻找?
那么究竟什么是迭代器呢?在大多数情况下,它只是一个带有一点包装或索引的指针。全部。因此,如果我们有一个元素的迭代器,那么我们只需要解引用(在大多数情况下,我们已经有一个指向元素的指针或者它是基本计算的)。因此,我们可以假设在大多数情况下,通过迭代器获取元素是在恒定时间内发生的。
有一些特殊情况,例如通过迭代器访问文件的内容,或者如果我们有一个“计算容器”(有时不存储所有元素,而是根据需要简单计算,例如“向量素数”),但这是一个单独的主题,与它无关。
现在让我们继续讨论在许多容器中都可以找到的 at 函数。根据标准,它应该检查是否超出容器,如果出现问题,则抛出异常。然后,如果所有检查都通过了,那么它可以简单地转到
operator[]
,这不需要进行越界检查(尽管在项目的调试版本中,工作室可以添加这样的检查来捕捉可能的问题道路 :))。也就是说,at通常包含一个条件+一个可能的异常和元素位置的计算。有许多公司认为,即使能够抛出异常也会导致性能下降几个百分点,从而导致数百万美元的损失。
如果您阅读 deque 文档,它会说访问一个元素通常是两个取消引用。
翻译成一种简单的语言,我们可以假设 deque 是一个向量或向量列表,并且为了通过索引获取一个元素,您需要存储每个数组的开头索引并至少找到它(并且这充其量是二进制搜索),但同时每次添加/删除时,您都需要更新索引,或者只是存储大小,但是您首先需要线性运行顶层数组找到存储适当内部元素的元素。无论如何,它不是很快。但是使用迭代器很简单——我们存储两个数字,你就完成了。我们需要继续(make ++)——我们增加了一个计数器,检查我们是否超出了限制,增加了第二个计数器,并将第一个计数器增加到零。
但它真的会更快吗?您必须进行基准测试并查看。也许在你的情况下,情况会相反。
结论 - 对于双端队列,迭代器最有可能是最快的,然后是 operator[],最后是。