Isn';t呼叫列表<;T>;::end()`低效? 在C++程序设计书中,我看到了下面的代码 STD::列表< /C>迭代器:< /P> for (iterator = list.start(); iterator != list.end(); iterator++)
一直调用Isn';t呼叫列表<;T>;::end()`低效? 在C++程序设计书中,我看到了下面的代码 STD::列表< /C>迭代器:< /P> for (iterator = list.start(); iterator != list.end(); iterator++),c++,iterator,C++,Iterator,一直调用list.end()不是效率低下吗?是否将保存到另一个变量更好,还是C++编译器(即G++)自动处理这个问题?< /P> < P> >代码>清单::()/代码>应该具有恒定的时间复杂性,特别是对于链表,这意味着它可能是非常有效的。 如果您的算法允许的话,存储值可能会稍微高效一些(同样,对于特别是链表,差异不大) 哦,而且一定要阅读Steve Jessop关于自己测试效率的回答 实际上,对于STL容器,container::end()非常便宜。事实上,C++标准强>任务> 算法的复杂性,对
list.end()
不是效率低下吗?是否将保存到另一个变量更好,还是C++编译器(即G++)自动处理这个问题?< /P> < P> >代码>清单::()/代码>应该具有恒定的时间复杂性,特别是对于链表,这意味着它可能是非常有效的。
如果您的算法允许的话,存储值可能会稍微高效一些(同样,对于特别是链表,差异不大)
哦,而且一定要阅读Steve Jessop关于自己测试效率的回答 实际上,对于STL容器,
container::end()
非常便宜。事实上,C++标准<>强>任务> <强>算法的复杂性,对于几种类型的方法(如果不是全部),和list.end()
调用可能最终成为字段访问,这在x86平台上并不比存储在堆栈上更昂贵
您的里程数可能因其他系列而异,但可以肯定的是,
list.end()
最终不会成为您的瓶颈。这不太可能有什么不同
标准容器函数是内联的,因此应该没有明显的函数调用开销。剩下的问题是优化器是否足够聪明,以避免不必要的开销,而这些开销对于执行比较来说并不是绝对必要的。例如:它实际上是否创建了一个临时的list::iterator
对象,填写其当前位置字段,然后将该字段读回,或者比较结果是否只是iterator
中的值与列表头中的值之间的指针比较
即使有一些不必要的开销,与增加迭代器相比,它可能可以忽略不计,与循环体相比,它甚至可以忽略不计
你可以测试它,这比猜测更可靠。记住启用优化——在没有优化的情况下测试性能有点像说,如果Blake从预热跑道走到公共汽车上的速度更快,Blake必须比Bolt快。一般来说,不,这不是低效的
end()
通常是一个内联函数,编译器将生成好的代码来执行它所做的任何操作。更重要的是,与什么相比效率低下?是的,您可以添加代码来创建一个变量来保存结果,这可能比简单地调用end()
要快一点,也可能不会快一点。这样的改变似乎不太可能产生足够大的速度差异,从而将太慢的程序转变为满足要求的程序。如果需要微优化,可以
一般来说,调用list.end()
不会对性能造成严重影响,也可能不会造成问题。它可能在每次调用时返回相同的值,也可能是内联的,等等。虽然速度不慢,但确实需要一些时间
如果您确实需要速度,您希望使用for(迭代器=list.start(),end=list.end;iteration!=end;++iterator)
。这将缓存结束迭代器(并进行预inc),并且不应重复调用
第二种类型通常是不必要的,但是如果
.end()
很昂贵或者循环非常大,则可能会很有用。调用std::list::end()
不太可能是一个大的效率问题,可能只是读取一个值。但是,您可以通过将编译器存储为变量来提示它不打算更改。对于其他容器,除了读取稍微复杂一点的基址之外,还可能涉及计算。仍然没有什么戏剧性的,但可能值得避免
但是,请注意,它也可能会更改循环的语义:如果循环的主体可以附加元素,则前端可能会移动。有趣的是,我在标准中没有发现任何特定的要求,说明在将元素插入容器时,
std::list::end()
是否会发生变化(我可以想象实现在哪些地方会发生变化,而在哪些地方不会发生变化;不过,很可能不会发生变化)。如果您想在修改列表时获得有保证的行为,您可以在每次迭代中调用list.end()
顺便说一句,关于使用
iterator++
而不是++iterator
,我有一个更大的性能问题,尤其是作者在书中使用的。尽管如此,这是一种微观优化,就像存储list.end()的结果一样,但这是一种廉价的优化。虽然过早优化是有害的,但良好的习惯不是。如果您不希望循环终止条件发生更改,即您不更改容器,则可以使用此模式:
for (mylist::iterator it = alist.begin(), finish = alist.end(); it != finish; ++it)
如果编译器无法确定容器没有发生更改,则不太可能为您进行此优化
请注意,这不太可能产生可测量的定时差异,但不会造成伤害。在
std::list
上不缓存end()
的一个好理由是它可以防止您犯以下错误:
for (iterator = list.rstart(), end = list.rend(); iterator != end; iterator++) {
// modify list
当您在
std::list
中进行修改时,迭代器不会失效,但是rend
不是一个哨兵(它指向下列表的第一个元素),这意味着如果您附加到反向列表的末尾(也称为未反向列表的开头的前置),它将不再是列表的末尾std::list::end
是双端链表的末尾,因此它应该是一个常量时间操作。给定y