在c++中为循环编写迭代器的更好方法是什么?
what is the better way to write iterators for a loop in C++
对于一个非常简单的事情,比如打印向量中的每个元素,在c++中使用什么更好的方法?
我一直在使用这个:
for (vector<int>::iterator i = values.begin(); i != values.end(); ++i)
之前,但在Boost::文件系统的一个例子中,我看到过这样:
for (vec::const_iterator it(v.begin()), it_end(v.end()); it != it_end; ++it)
对我来说,它看起来更复杂,我不明白为什么它比我一直在使用的那个更好。
你能告诉我为什么这个版本更好吗?或者对于简单的事情,比如打印向量的元素,它并不重要?
i != values.end()
是否使迭代变慢?
还是const_iterator
vs iterator
?const_iterator在这样的循环中更快吗?
-
Foo x = y;
和Foo x(y);
是等价的,所以用你喜欢的。 -
将
end
提升到循环之外可能是编译器会做的事情,也可能不是,无论如何,它都表明容器端没有改变。 -
如果不打算修改元素,则使用const-iterator,因为这就是它们的含义。
for (MyVec::const_iterator it = v.begin(), end = v.end(); it != end; ++it)
{
/* ... */
}
在c++ 0x中使用auto
+ cbegin()
:
for (auto it = v.cbegin(), end = v.cend(); it != end; ++it)
(也许你想用一个现成的容器打印机?)
for (vector<int>::iterator i = values.begin(); i != values.end(); ++i)
…和…
for (vec::const_iterator it(v.begin()), it_end(v.end()); it != it_end; ++it)
对我来说[后者,在boost中看到]看起来更复杂,我不明白为什么它比我一直在使用的那个更好。
我想说,对于那些没有特定理由喜欢后者到扭曲感知的程度的人来说,它看起来会更复杂。但让我们继续看看为什么它可能更好....
你能告诉我为什么这个版本更好吗?或者对于像打印向量元素这样简单的事情,它并不重要?i != values.end()会使迭代变慢吗?
it_end
性能:
it_end
只获得一次end()
值作为循环的开始。对于任何计算end()
有点昂贵的容器,只调用它一次可以节省CPU时间。对于任何像样的真实c++标准库,所有end()
函数都不执行计算,并且可以内联以获得相同的性能。在实践中,除非有一些机会,您可能需要在一个非标准容器中添加一个更昂贵的end()
函数,否则在优化代码中显式"缓存"end()
没有任何好处。
这很有趣,因为这意味着对于vector
,size()
可能需要一个小的计算-概念上从end()
减去begin()
,然后除以sizeof(value_type)
(编译器在指针算术期间隐式地按大小缩放),例如GCC 4.5.2:size_type size() const
{ return size_type(this->_M_impl._M_finish - this->_M_impl._M_start); }
Maintenance:如果代码演变为在循环中插入或擦除元素(很明显,迭代器本身不会失效——对于映射/集合/列表等来说是合理的),如果缓存的
end()
值也需要显式地重新计算,那么这是一个维护点(因此容易出错)。
这是一个小细节,但是这里的
vec
必须是typedef
,并且我认为通常最好使用容器类型定义,因为它放松了容器类型与访问迭代器类型的耦合。type identifier(expr)
风格和文档重点:
type identifier(expr)
比type identifier = expr
更直接地指示构造函数调用,这是一些人喜欢这种形式的主要原因。我通常更喜欢后者,因为我喜欢强调赋值的意义。它在视觉上是明确的,而函数调用表示法用于许多事情。接近等价:对于大多数类,无论如何都调用相同的构造函数,但是如果
type
有一个来自expr
类型的显式构造函数,那么如果使用=
,它将被忽略。更糟糕的是,其他一些转换可能允许使用不太理想的构造函数。例如,X x = 3.14;
,将通过explicit X::X(double);
来匹配X::X(int)
-你可能会得到一个不太精确的(或者只是完全错误的)结果-但我还没有被这样的问题咬过,所以它是相当理论化的!
还是const_iterator vs iterator?const_iterator在这样的循环中更快吗?
对于标准容器,const_iterator
和iterator
执行相同,但后者意味着您希望能够在迭代时修改元素。使用const_iterator
文档时,如果您不打算这样做,编译器将捕获任何试图修改迭代器的矛盾用法。例如,当您打算对迭代器本身进行自增时,就不可能意外地对迭代器地址的值进行自增。
考虑到c++ 0x在其他答案中已经被提到——但只是auto
和cbegin
/cend
的增量好处——还有一个新的符号支持:
for (const Foo& foo: container)
// use foo...
要在矢量中打印项,您不应该使用上述任何一种方法(至少在我看来)。
我建议这样做:
std::copy(values.begin(), values.end(),
std::ostream_iterator<T>(std::cout, "n"));
您可以通过index
访问它们int main(int argc, char* argv[])
{
std::vector<int> test;
test.push_back(10);
test.push_back(11);
test.push_back(12);
for(int i = 0; i < test.size(); i++)
printf("%dn", test[i]);
}
打印出:101112
我认为这无关紧要。在内部,它们做同样的事情,所以你的编译器应该优化它。我个人会使用第一个版本,因为我发现它更清晰,因为它紧跟for循环结构。
for (vector<int>::iterator i = values.begin(); i != values.end(); ++i)
- 初始化具有非默认构造函数的std::数组项的更好方法
- Protobuf中重复字段的问题.使用重复字段进行序列化/反序列化的更好方法是什么?
- 编写按初始值循环的循环的更好方法是什么
- 用 c++ 为游戏制作"bullet"的更好方法?
- 在CMakeLists中包含目录的更好方法.txt
- 将QDomDocument数据用作文本的更好方法
- C++ - 创建具有相同字符的特定大小的以 null 结尾的 c 样式字符串的更好方法
- 在 sqlite3 中批量插入的更好方法C++
- 设计许多单例代码结构的更好方法
- 使用继承的类模板避免公共成员不可见和源代码膨胀/重复的更好方法
- 在初始化列表中初始化数组的更好方法
- 将对象从一个 std::d eque 移动到另一个的更好方法
- 请告诉我在巴泽尔拥有多平台工作空间的更好方法
- 构造具有大量数据的对象的更好方法(C++)
- 从2D矢量中找出最小尺寸的向量元素的更好方法
- 在C++中创建不可变对象的更好方法
- 在构造函数中组织初始值设定项列表的更好方法
- std::copy with return values - 防止"expression: string iterators incompatible"的更好方法?
- 删除数组成员的更好方法是什么?
- 在C 中操作数据的更好方法是什么