C++ ++;它或它++;在地图上迭代时?

C++ ++;它或它++;在地图上迭代时?,c++,iterator,iteration,stdmap,C++,Iterator,Iteration,Stdmap,演示如何在std::map上迭代的示例通常如下所示: MapType::const_iterator end = data.end(); for (MapType::const_iterator it = data.begin(); it != end; ++it) i、 e.它使用++it而不是it++。有什么原因吗?如果改用it++会有什么问题吗?it++返回上一个迭代器的副本。由于未使用此迭代器,因此这是浪费++it返回对递增迭代器的引用,避免复制 请参阅以获得更全面的解释。使用增量前

演示如何在
std::map
上迭代的示例通常如下所示:

MapType::const_iterator end = data.end(); 
for (MapType::const_iterator it = data.begin(); it != end; ++it)

i、 e.它使用
++it
而不是
it++
。有什么原因吗?如果改用
it++
会有什么问题吗?

it++
返回上一个迭代器的副本。由于未使用此迭代器,因此这是浪费<代码>++it返回对递增迭代器的引用,避免复制

请参阅以获得更全面的解释。

使用增量前运算符与增量后运算符有细微差别。在设置使用迭代器的循环时,应选择使用预增量:

for (list<string>::const_iterator it = tokens.begin();
    it != tokens.end();
    ++it) { // Don't use it++
    ...
}
如您所见,增量后实现涉及两个额外的复制操作。如果所讨论的对象体积较大,那么这可能相当昂贵。话虽如此,一些编译器可能足够聪明,可以通过优化实现单拷贝操作。关键是后增量通常比前增量涉及更多的工作,因此,明智的做法是习惯于将“+”放在迭代器之前,而不是之后


(1) 链接网站的功劳。

从逻辑的角度来看,这是一样的,在这里并不重要

为什么使用前缀1-因为它更快-它更改迭代器并返回其值,而后缀创建临时对象,递增当前迭代器,然后返回临时对象(递增之前相同迭代器的副本)。因为没有人在这里观察这个临时对象(返回值),所以它是相同的(逻辑上)

编译器很有可能会对此进行优化


此外-实际上,对于任何类型的都应该是这样的。但这只是应该的。任何人都可以重载操作符+++——后缀和前缀,它们可能会产生副作用和不同的行为


嗯,这是一件可怕的事情,但仍然有可能。

这不会导致任何问题,但使用
++It
更为正确。对于小类型,使用
++i
i++
并不重要,但对于“大”类:


编译器可能会对其中的一些进行优化,但很难确定。

为了进行测试,我制作了三个源文件:

#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 ###
#包括
结构Foo{inta;double b;char c;};
typedef std::map FMap;
###仅文件1###
无效集(FMap&m、常量Foo&f)
{
for(FMap::iterator it=m.begin(),end=m.end();it!=end;+it)
它->秒=f;
}
###仅文件2###
无效集(FMap&m、常量Foo&f)
{
for(FMap::iterator it=m.begin();it!=m.end();++it)
它->秒=f;
}
###仅限文件3###
无效集(FMap&m、常量Foo&f)
{
for(FMap::iterator it=m.begin();it!=m.end();it++)
它->秒=f;
}
###结束###
在使用GCC4.6.1中的
g++-S-O3
进行编译后,我发现版本2和版本3生成相同的程序集,而版本1只在一条指令上不同,
cmpl%eax,%esi
vs
cmpl%esi,%eax


所以,选择适合你的风格的东西。Prefix increment
++it
可能是最好的,因为它最准确地表达了您的需求,但不要挂断它。

正如其他答案所说,除非它在上下文中不起作用,否则更喜欢它。对于在小类型的容器上进行迭代,它实际上没有什么区别(如果编译器对其进行优化,则没有区别),但是对于大类型的容器,它可以产生区别,因为它可以节省制作副本的成本


诚然,在特定的上下文中,您可能知道该类型足够小,因此不必担心它。但是稍后,您团队中的其他人可能会将容器的内容更改为重要的内容。另外,我认为最好让自己养成一个好习惯,只有在你知道必须的时候才发布增量。

看看[这个][1]。〔1〕:如果在C++中任意长度的代码, ++IT 将大大提高效率。不是在你的代码中,而是花时间向人们解释你为什么没有做
++it
。既然它真的一点也不重要,你不妨用不会引起争论的方式来做。@Dennis:这没什么大不了的,而且常常是不可测量的,但在一个紧密的循环中,它可以产生影响。@Christopher:人们都这么说,但从来没有人举过什么时候发生的例子。我运行过的每个测试都会导致生成的代码几乎完全相同,即使在“紧循环”中也不值得担心。唯一的例外是当增量运算符足够复杂,无法内联时[因此,无关的临时运算符没有被删除]。@Dennis:我应该对其进行更多限定,是的。我说的是复杂迭代器的不寻常情况,这几乎无关紧要。这个问题纯粹是风格的问题。@Dark-因为它没有被使用,编译器可能会注意到这一点并优化副本。我知道这一点。然而,显然编译器不需要这样做。我个人认为增量前的读取效果更好。把“++”读作“increment”,它会在你的脑海中变成“increment it”,而不是“it increment”,我不会说这是风格的问题。增量前说“增量”,增量后说“增量,但把原始值还给我,这样我就可以用它做点什么。”如果你没有用原始值做点什么,那么增量后就不能清楚地表达你的意图。这的确是一件可怕的事。类似于滥用移位运算符将数据放入流中,对吗?;-)(SCNR)好吧,这是一个很好的观点——毕竟,你最终可能会遇到一个定制迭代器,无论出于何种原因,它的复制成本非常高,而且无法优化,所以习惯性地写下你的意思(
++it
),而不是
operator++(type x,int){
    type tmp=x; //need copy
    ++x;
    return tmp;
}
#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 ###