12

通常你会发现这样的 STL 代码:

for (SomeClass::SomeContainer::iterator Iter = m_SomeMemberContainerVar.begin(); Iter != m_SomeMemberContainerVar.end(); ++Iter)
{
}

但我们实际上有这样的建议:

SomeClass::SomeContainer::iterator Iter = m_SomeMemberContainerVar.begin();
SomeClass::SomeContainer::iterator IterEnd = m_SomeMemberContainerVar.end();
for (; Iter != IterEnd; ++Iter)
{
}

如果您担心范围,请添加括号:

{
    SomeClass::SomeContainer::iterator Iter = m_SomeMemberContainerVar.begin();
    SomeClass::SomeContainer::iterator IterEnd = m_SomeMemberContainerVar.end();
    for (; Iter != IterEnd; ++Iter)
    {
    }
}

这应该可以提高速度和效率,尤其是在您对控制台进行编程时,因为在循环的每次迭代中都不会调用 .end() 函数。我只是认为性能改进是理所当然的,这听起来很合理,但我不知道有多少,这当然取决于容器的类型和实际使用的 STL 实现。但是在使用这种风格几个月后,我实际上比第一种更喜欢它。

原因是可读性:for 行整洁。在实际生产代码中使用限定符和成员变量,如果您使用第一个示例中的样式,很容易使行很长。这就是为什么我在这个例子中故意让它有一个水平滚动条,只是为了让你明白我在说什么。;)

另一方面,您突然将 Iter 变量引入 for 循环的外部范围。但是,至少在我工作的环境中,即使在第一个示例中,Iter 也可以在外部范围内访问。

你对此有什么看法?除了可能限制Iter的范围之外,第一种风格是否有任何专业人士?

4

13 回答 13

14

如果您将代码正确地包装成行,则内联表单将同样具有可读性。此外,您应该始终将其iterEnd = container.end()作为优化:

for (SomeClass::SomeContainer::iterator Iter = m_SomeMemberContainerVar.begin(),
    IterEnd = m_SomeMemberContainerVar.end();
    Iter != IterEnd;
    ++Iter)
{
}

更新:根据 paercebal 的建议修复了代码。

于 2008-10-09T20:29:28.123 回答
9

另一种选择是使用 foreach 宏,例如boost foreach

BOOST_FOREACH( ContainedType item, m_SomeMemberContainerVar )
{
   mangle( item );
}

我知道在现代 c++ 中不鼓励使用宏,但是在 auto 关键字广泛使用之前,这是我发现的获得简洁易读的东西的最佳方式,并且仍然是完全类型安全和快速的。您可以使用任何一种初始化样式来实现您的宏,以获得更好的性能。

链接页面上还有一条关于将 BOOST_FOREACH 重新定义为 foreach 以避免烦人的全部大写的注释。

于 2008-10-09T20:43:50.827 回答
5

如果在 for 循环之后不需要迭代器,则第一种形式(在 for 循环内)更好。它将其范围限制为 for 循环。

我严重怀疑无论哪种方式都可以提高效率。也可以使用 typedef 使其更具可读性。

typedef SomeClass::SomeContainer::iterator MyIter;

for (MyIter Iter = m_SomeMemberContainerVar.begin(); Iter != m_SomeMemberContainerVar.end(); ++Iter)
{
}

我会推荐更短的名字;-)

于 2008-10-09T20:25:40.083 回答
4

iter.end()不,在循环开始之前暂停是个坏主意。如果您的循环更改了容器,那么结束迭代器可能会失效。此外,该end()方法保证为 O(1)。

过早的优化是万恶之源。

此外,编译器可能比你想象的更聪明。

于 2008-10-11T16:02:01.130 回答
4

在 -O2 优化的 g++ 中看过这个(只是为了具体一点)

std::vector、std::list 和 std::map(和朋友)的生成代码没有区别。std::deque 的开销很小。

所以总的来说,从性能的角度来看,它几乎没有什么区别。

于 2008-10-11T16:10:48.227 回答
1

尽管迭代器生命周期会使我倾向于 for-scoped 版本,但我对此并没有特别强烈的意见。

但是,可读性可能是一个问题。这可以通过使用 typedef 来帮助,因此迭代器类型更易于管理:

typedef SomeClass::SomeContainer::iterator sc_iter_t;

for (sc_iter_t Iter = m_SomeMemberContainerVar.begin(); Iter != m_SomeMemberContainerVar.end(); ++Iter)
{
}

不是很大的改进,但有点。

于 2008-10-09T20:30:44.953 回答
1

我没有任何控制台经验,但在大多数现代 C++ 编译器中,除了范围问题之外,任何一个选项最终都是等效的。Visual Studio 编译器几乎总是在调试代码中将条件比较放在隐式临时变量(通常是寄存器)中。因此,虽然从逻辑上看,end() 调用似乎是通过每次迭代进行的,但优化后的编译代码实际上只调用一次,并且比较是在循环中每次后续时间完成的唯一事情。

在控制台上可能不是这种情况,但您可以拆开循环来检查优化是否正在发生。如果是,那么您可以选择您喜欢的任何风格或组织中的标准风格。

于 2008-10-09T20:43:20.670 回答
1

它可能会导致代码脱节,但我也喜欢将其拉出到一个单独的函数中,并将两个迭代器都传递给它。

doStuff(coll.begin(), coll.end())

并且有..

template<typename InIt>
void doStuff(InIt first, InIt last)
{
   for (InIt curr = first; curr!= last; ++curr)
   {
       // Do stuff
   }
 }

喜欢的东西:

  • 永远不必提及丑陋的迭代器类型(或考虑它是 const 还是非 const)
  • 如果每次迭代都没有调用 end() 有好处,我明白了

不喜欢的事情:

  • 分解代码
  • 附加函数调用的开销。

但是有一天,我们会有 lambdas!

于 2008-10-09T21:19:32.283 回答
1

我不认为这是一种糟糕的风格。只需使用 typedefs 即可避免 STL 冗长和冗长的行。

typedef set<Apple> AppleSet;
typedef AppleSet::iterator  AppleIter;
AppleSet  apples;

for (AppleIter it = apples.begin (); it != apples.end (); ++it)
{
   ...
}

Spartan Programming是减轻风格问题的一种方法。

于 2008-10-12T04:17:45.390 回答
0

如果您担心范围,可以在初始化和循环周围加上大括号。通常我要做的是在函数的开头声明迭代器并在整个程序中重用它们。

于 2008-10-09T20:27:35.053 回答
0

我发现第二个选项更具可读性,因为您最终不会得到一条大线。然而,费鲁乔提出了一个关于范围的好观点。

于 2008-10-09T20:27:55.257 回答
0

我同意费鲁乔的观点。有些人可能更喜欢第一种样式,以便将 end() 调用拉出循环。

我还可以补充一点,C++0x 实际上会使两个版本都更干净:

for (auto iter = container.begin(); iter != container.end(); ++iter)
{
   ...
}

auto iter = container.begin();
auto endIter = container.end();
for (; iter != endIter; ++iter)
{
   ...
}
于 2008-10-09T20:28:07.533 回答
0

我通常会写:

SomeClass::SomeContainer::iterator Iter = m_SomeMemberContainerVar.begin(),
                                   IterEnd = m_SomeMemberContainerVar.end();

for(...)
于 2008-10-09T20:29:08.420 回答