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