[英]C++ STL: Which method of iteration over a STL container is better?
这对你们中的一些人来说可能看起来很无聊,但是对于STL容器,以下哪两种迭代方法更好? 为什么 ?
class Elem;
typedef vector<Elem> ElemVec;
ElemVec elemVec;
// Method 0
for (ElemVec::iterator i = elemVec.begin(); i != elemVec.end(); ++i)
{
Elem& e = *i;
// Do something
}
// Method 1
for (int i = 0; i < elemVec.size(); ++i)
{
Elem& e = elemVec.at(i);
// Do something
}
方法0似乎更清晰STL,但方法1使用较少的代码实现相同。 在容器上简单的迭代是什么似乎遍布任何源代码的地方。 所以,我倾向于选择方法1,这似乎可以减少视觉混乱和代码大小。
PS:我知道迭代器可以做的不仅仅是一个简单的索引。 但是,请将回复/讨论集中在如上所示的容器上的简单迭代上。
第一个版本适用于任何容器,因此在将任何容器作为参数的模板函数中更有用。 即使对于矢量,它也可以略微提高效率。
第二个版本仅适用于矢量和其他整数索引容器。 对于那些容器来说,它更加惯用,C ++的新手很容易理解它,如果你需要对索引执行其他操作,这很有用,这种情况并不少见。
像往常一样,我担心没有简单的“这个更好”的答案。
如果你不介意(非常?)小的效率损失,我建议使用Boost.Foreach
BOOST_FOREACH( Elem& e, elemVec )
{
// Your code
}
方法0更快,因此建议使用。
方法1使用size(),允许为O(1),具体取决于容器和stl实现。
以下迭代标准库容器的方法是最好的。
使用C ++ 11 (及以后)的范围内,基于for
-loop与auto
符 :
// Method 2
for (auto& e: elemVec)
{
// Do something with e...
}
这与您的Method 0
类似,但需要更少的输入,更少的维护,并适用于与std::begin()
和std::end()
兼容的任何容器, 包括普通旧数组。
方法0的一些更多优点:
主要的缺点是,在许多情况下,您扫描两个容器,在这种情况下,索引比保留两个迭代器更清晰。
巧合的是我最近进行了速度测试(用rand()填充10 * 1024 * 1024英寸)。
这些是3次运行,时间以纳秒为单位
vect[i] time : 373611869
vec.at(i) time : 473297793
*it = time : 446818590
arr[i] time : 390357294
*ptr time : 356895778
更新:添加了stl-algorithm std :: generate,由于特殊的迭代器优化(VC ++ 2008),它似乎运行速度最快。 微秒的时间。
vect[i] time : 393951
vec.at(i) time : 551387
*it = time : 596080
generate = time : 346591
arr[i] time : 375432
*ptr time : 334612
结论:使用标准算法,它们可能比显式循环更快! (也是好的做法)
更新:以上时间处于I / O限制的情况下,我使用CPU绑定进行相同的测试(迭代一个相对较短的向量,它应该重复适合缓存,将每个元素乘以2并写回向量)
//Visual Studio 2008 Express Edition
vect[i] time : 1356811
vec.at(i) time : 7760148
*it = time : 4913112
for_each = time : 455713
arr[i] time : 446280
*ptr time : 429595
//GCC
vect[i] time : 431039
vec.at(i) time : 2421283
*it = time : 381400
for_each = time : 380972
arr[i] time : 363563
*ptr time : 365971
有趣的是,与for_each相比,VC ++中的迭代器和operator []相当慢(这似乎会降低迭代器的性能,通过一些模板魔法降低指针的性能)。
在GCC中,访问时间仅对at()更糟,这是正常的,因为它是测试中唯一的范围检查函数。
方法0,原因有几个。
当然,最好的解决方案通常是解决方案2:std算法之一。 std :: for_each,std :: transform,std :: copy或其他你需要的东西。 这有一些进一步的优点:
通常,避免过度指定代码。 准确指定您想要完成的任务,而不是其他内容。 std算法通常是去那里的方法,但即使没有它们,如果你不需要索引i
,为什么要这样做呢? 在这种情况下,请使用迭代器。
这取决于哪种类型的容器。 对于vector
,使用哪个可能无关紧要。 方法0已经变得更加惯用,但正如大家所说,它们并没有太大的区别。
如果你决定使用一个list
,相反,方法1原则上是O(N)
,但事实上at()
方法中没有列表,所以你甚至不能这样做。 (所以在某种程度上你的问题叠加在甲板上。)
但这本身就是方法0的另一个参数:它对不同的容器使用相同的语法。
上面没有考虑的可能性:取决于“做某事”的细节,可以同时使用方法0和方法1,您不必选择:
for (auto i = elemVec.begin(), ii = 0; ii < elemVec.size(); ++i, ++ii)
{
// Do something with either the iterator i or the index ii
}
这样,找到索引或访问相应的成员都是以微不足道的复杂性获得的。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.