显示如何迭代std::map
的示例通常是这样的:
MapType::const_iterator end = data.end();
for (MapType::const_iterator it = data.begin(); it != end; ++it)
即。它使用++it
代替it++
。有什么理由吗?如果我改用it++
会不会有任何问题?
答案 0 :(得分:20)
it++
返回前一个迭代器的副本。由于不使用此迭代器,因此这很浪费。 ++it
返回对递增迭代器的引用,避免复制。
请参阅Question 13.15以获得更全面的解释。
答案 1 :(得分:11)
进行测试,我制作了三个源文件:
#include <map>
struct Foo { int a; double b; char c; };
typedef std::map<int, Foo> FMap;
### File 1 only ###
void Set(FMap & m, const Foo & f)
{
for (FMap::iterator it = m.begin(), end = m.end(); it != end; ++it)
it->second = f;
}
### File 2 only ###
void Set(FMap & m, const Foo & f)
{
for (FMap::iterator it = m.begin(); it != m.end(); ++it)
it->second = f;
}
### File 3 only ###
void Set(FMap & m, const Foo & f)
{
for (FMap::iterator it = m.begin(); it != m.end(); it++)
it->second = f;
}
### end ###
使用g++ -S -O3
,GCC 4.6.1编译后,我发现版本2和3生成了相同的程序集,而版本1只在一条指令cmpl %eax, %esi
vs cmpl %esi, %eax
。
所以,请选择并使用适合您风格的任何内容。前缀增量++it
可能是最好的,因为它最准确地表达了您的要求,但不要挂断它。
答案 2 :(得分:7)
使用预增量运算符与后增量运算符有轻微的performance advantage。在设置使用迭代器的循环时,您应该选择使用预增量:
for (list<string>::const_iterator it = tokens.begin();
it != tokens.end();
++it) { // Don't use it++
...
}
当您考虑如何实施两种运营商时,原因就会浮出水面。预增量非常简单。但是,为了使后增量起作用,您需要首先制作对象的副本,对原始对象执行实际增量,然后返回副本:
class MyInteger {
private:
int m_nValue;
public:
MyInteger(int i) {
m_nValue = i;
}
// Pre-increment
const MyInteger &operator++() {
++m_nValue;
return *this;
}
// Post-increment
MyInteger operator++(int) {
MyInteger clone = *this; // Copy operation 1
++m_nValue;
return clone; // Copy operation 2
}
}
如您所见,增量后实现涉及两个额外的复制操作。如果所讨论的物体体积庞大,这可能非常昂贵。话虽如此,一些编译器可能足够智能,可以通过优化来完成单个复制操作。关键是后增量通常涉及比预增量更多的工作,因此习惯将“++”放在迭代器之前而不是之后是明智的。
(1)归功于链接网站。
答案 3 :(得分:6)
从逻辑的角度来看 - 它是相同的,无关紧要 here 。
为什么使用前缀one - 因为它更快 - 它更改迭代器并返回其值,而后缀创建临时对象,递增当前迭代器,然后返回临时对象(同一迭代器的副本,在递增之前) )。由于没有人在这里观察这个临时对象(返回值),所以它(逻辑上)是相同的。
很有可能,编译器会对此进行优化。
另外 - 实际上,对于任何类型,这应该是这样的。但它应该是。任何人都可以重载operator++
- 后缀和前缀,它们可以有副作用和不同的行为。
嗯,这是一件可怕的事情,但仍有可能。
答案 4 :(得分:1)
它不会导致任何问题,但使用++it
更为正确。对于小类型,使用++i
或i++
并不是真正的问题,而是使用“大”类:
operator++(type x,int){
type tmp=x; //need copy
++x;
return tmp;
}
编译器可能会优化其中一些,但很难确定。
答案 5 :(得分:1)
正如其他答案所说,除非它不能在上下文中起作用,否则更喜欢它。对于小型类型的容器进行迭代,它确实没什么区别(或者如果编译器优化它就没有区别),但是对于大型容器,它可以通过节省制作副本的成本而产生差异。
没错,您可能会在特定情况下知道该类型足够小,因此您不必担心它。但是稍后,团队中的其他人可能会将容器的内容更改为重要的位置。另外,我认为最好让自己养成一个好习惯,只有在你知道必须的时候才增加后期。