在c++中为循环编写迭代器的更好方法是什么?

what is the better way to write iterators for a loop in C++

本文关键字:更好 方法 是什么 迭代器 c++ 循环      更新时间:2023-10-16

对于一个非常简单的事情,比如打印向量中的每个元素,在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在这样的循环中更快吗?

  1. Foo x = y;Foo x(y);是等价的,所以用你喜欢的。

  2. end提升到循环之外可能是编译器会做的事情,也可能不是,无论如何,它都表明容器端没有改变。

  3. 如果不打算修改元素,则使用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_iteratoriterator执行相同,但后者意味着您希望能够在迭代时修改元素。使用const_iterator文档时,如果您不打算这样做,编译器将捕获任何试图修改迭代器的矛盾用法。例如,当您打算对迭代器本身进行自增时,就不可能意外地对迭代器地址的值进行自增。

考虑到c++ 0x在其他答案中已经被提到——但只是autocbegin/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)