在迭代地图时,它或它是++?

lau*_*ent 7 c++ iteration iterator stdmap

显示如何迭代a的示例std::map通常是这样的:

MapType::const_iterator end = data.end(); 
for (MapType::const_iterator it = data.begin(); it != end; ++it)
Run Code Online (Sandbox Code Playgroud)

即它用来++it代替it++.有什么理由吗?如果我使用它会有任何问题it++吗?

Dar*_*con 20

it++ 返回前一个迭代器的副本.由于不使用此迭代器,因此这很浪费.++it返回对递增迭代器的引用,避免复制.

有关更全面的解释,请参阅问题13.15.

  • 这几乎不重要.问题纯粹是风格. (4认同)
  • 我知道这一点.但是,显然编译器不需要这样做. (2认同)

Ker*_* SB 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 ###
Run Code Online (Sandbox Code Playgroud)

与编译后g++ -S -O3,GCC 4.6.1,我发现,版本2和3产生相同的组件,和版本1的区别仅在于一个指令,cmpl %eax, %esiVS cmpl %esi, %eax.

所以,请选择并使用适合您风格的任何东西.前缀增量++it可能是最好的,因为它最准确地表达了您的要求,但不要挂断它.


小智 7

使用预增量运算符与后增量运算符相比,性能略有提升.在设置使用迭代器的循环时,您应该选择使用预增量:

for (list<string>::const_iterator it = tokens.begin();
    it != tokens.end();
    ++it) { // Don't use it++
    ...
}
Run Code Online (Sandbox Code Playgroud)

当您考虑如何实施两种运营商时,原因就会浮出水面.预增量非常简单.但是,为了使后增量起作用,您需要首先制作对象的副本,对原始对象执行实际增量,然后返回副本:

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
    }
}
Run Code Online (Sandbox Code Playgroud)

如您所见,增量后实现涉及两个额外的复制操作.如果所讨论的物体体积庞大,这可能非常昂贵.话虽如此,一些编译器可能足够智能,可以通过优化来完成单个复制操作.关键在于后增量通常涉及比预增量更多的工作,因此习惯于将"++"放在迭代器之前而不是之后.

(1)信用链接网站.


Kir*_*rov 6

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

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

有一个非常大的机会,编译器将优化它.


另外 - 实际上,对于任何类型,这应该是这样的.但它应该是.任何人都可以重载operator++- 后缀和前缀,它们可以有副作用和不同的行为.

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