++它还是它++当迭代地图时?

发布于 2024-11-27 20:04:31 字数 273 浏览 2 评论 0原文

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

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

即它使用 ++it 而不是 it++。有什么理由吗?如果我改用 it++ 会有什么问题吗?

Examples showing how to iterate over a std::map are often like that:

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

i.e. it uses ++it instead of it++. Is there any reason why? Could there be any problem if I use it++ instead?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

别忘他 2024-12-04 20:04:31

it++ 返回前一个迭代器的副本。由于没有使用这个迭代器,这是浪费的。 ++it 返回对递增迭代器的引用,避免复制。

请参阅问题 13.15 了解更完整的说明。

it++ returns a copy of the previous iterator. Since this iterator is not used, this is wasteful. ++it returns a reference to the incremented iterator, avoiding the copy.

Please see Question 13.15 for a fuller explanation.

眼角的笑意。 2024-12-04 20:04:31

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

#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, %esicmpl %esi, %eax

因此,请选择并使用适合您风格的任何东西。前缀增量 ++it 可能是最好的,因为它最准确地表达您的需求,但不要为此烦恼。

Putting it to the test, I made three source files:

#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 ###

After compiling with g++ -S -O3, GCC 4.6.1, I find that version 2 and 3 produce identical assembly, and version 1 differs only in one instruction, cmpl %eax, %esi vs cmpl %esi, %eax.

So, take your pick and use whatever suits your style. Prefix increment ++it is probably best because it expresses your requirements most accurately, but don't get hung up about it.

め七分饶幸 2024-12-04 20:04:31

使用预增量运算符有轻微的性能优势与后自增运算符相对。在设置使用迭代器的循环时,您应该选择使用预增量:

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) 出自链接网站。

There’s a slight performance advantage in using pre-increment operators versus post-increment operators. In setting up loops that use iterators, you should opt to use pre-increments:

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

The reason comes to light when you think about how both operators would typically be implemented.The pre-increment is quite straightforward. However, in order for post-increment to work, you need to first make a copy of the object, do the actual increment on the original object and then return the copy:

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
    }
}

As you can see, the post-increment implementation involves two extra copy operations. This can be quite expensive if the object in question is bulky. Having said that, some compilers may be smart enough to get away with a single copy operation, through optimization. The point is that a post-increment will typically involve more work than a pre-increment and therefore it’s wise to get used to putting your “++”s before your iterators rather than after.

(1) Credit to linked website.

极致的悲 2024-12-04 20:04:31

从逻辑的角度来看 - 这是相同的,并且在这里并不重要。

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

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


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

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

At logical point of view - it's the same and it doesn't matter here.

Why the prefix one is used - because it's faster - it changes the iterator and returns its value, while the postfix creates temp object, increments the current iterator and then returns the temp object (copy of the same iterator, before the incrementing ). As no one watches this temp object here (the return value), it's the same (logically).

There's a pretty big chance, that the compiler will optimize this.


In Addition - actually, this is supposed to be like this for any types at all. But it's just supposed to be. As anyone can overload operator++ - the postfix and prefix, they can have side effects and different behavior.

Well, this is a horrible thing to do, but still possible.

夜还是长夜 2024-12-04 20:04:31

它不会造成任何问题,但使用 ++it 更正确。对于小类型,使用 ++ii++ 并不重要,但对于“大”类:

operator++(type x,int){
    type tmp=x; //need copy
    ++x;
    return tmp;
}

编译器可能会优化其中一些,但这很难可以肯定的是。

It won't cause any problems, but using ++it is more correct. With small types it doesn't really matter to use ++i or i++, but for "big" classes:

operator++(type x,int){
    type tmp=x; //need copy
    ++x;
    return tmp;
}

The compiler may optimize out some of them, but it's hard to be sure.

魔法少女 2024-12-04 20:04:31

正如其他答案所说,更喜欢 ++it ,除非它在上下文中不起作用。对于迭代小型类型的容器,它确实没有什么区别(或者如果编译器将其优化掉,则没有区别),但对于大型类型的容器,由于节省了制作副本的成本,它可以产生影响。

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

As other answers have said, prefer ++it unless it won't work in context. For iterating over containers of small types it really makes little difference (or no difference if the compiler optimizes it away), but for containers of large types it can make a difference due to saving the cost of making a copy.

True, you might know in your specific context that the type is small enough so you don't worry about it. But later, someone else on your team might change the contents of the container to where it would matter. Plus, I think it is better to get yourself into a good habit, and only post-increment when you know you must.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文