是迭代初始化内部为环认为是糟糕的风格,并为什么?
-
06-07-2019 - |
题
通常你会发现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执行情况中使用。但具有使用这种风格几个月现在我真的喜欢它第一次呢。
其原因可读性:该线是整洁。与预赛和成员的变量在实际生产的代码这是很容易的 真的 长线条如果您使用的风格在第一个例子。这就是为什么我有意地作出它有一个滚动水平,在此示例中,只是让你看到我在说什么.;)
另一方面,你突然介绍Iter变量的外部范围的循环。但是,至少在环境中工作,Iter会已经可以在外范围甚至在第一个例子。
什么是你带上这个吗?是否有任何亲的第一式比其他的可能范围的限制Iter?
解决方案
如果您正确地将代码包装成行,则内联表单将具有相同的可读性。此外,您应该始终将 iterEnd = container.end()
作为优化:
for (SomeClass::SomeContainer::iterator Iter = m_SomeMemberContainerVar.begin(),
IterEnd = m_SomeMemberContainerVar.end();
Iter != IterEnd;
++Iter)
{
}
更新:根据每个paercebal的建议修复代码。
其他提示
另一种选择是使用foreach宏,例如提升的foreach :
BOOST_FOREACH( ContainedType item, m_SomeMemberContainerVar )
{
mangle( item );
}
我知道在现代c ++中不鼓励使用宏,但是直到auto关键字被广泛使用,这才是我发现的最简单和可读的东西,并且仍然完全类型安全且快速。您可以使用任何初始化样式来实现宏,从而获得更好的性能。
链接页面上还有一条关于重新定义BOOST_FOREACH作为foreach的说明,以避免烦人的全部上限。
如果在for循环之后不需要迭代器,第一种形式(for循环内部)会更好。它将其范围限制为for循环。
我非常怀疑任何一种方式都可以提高效率。使用typedef也可以使其更具可读性。
typedef SomeClass::SomeContainer::iterator MyIter;
for (MyIter Iter = m_SomeMemberContainerVar.begin(); Iter != m_SomeMemberContainerVar.end(); ++Iter)
{
}
我建议使用较短的名字; - )
在g ++中观察了这个-O2优化(只是为了具体)
std :: vector,std :: list和std :: map(和朋友)的生成代码没有区别。 std :: deque的开销很小。
总的来说,从性能的角度来看,它没什么区别。
不,在循环开始之前暂停 iter.end()
是个坏主意。如果循环更改容器,则结束迭代器可能无效。此外, end()
方法保证为O(1)。
过早优化是万恶之源。
此外,编译器可能比您想象的更聪明。
我没有一种特别强烈的意见,尽管迭代器的生命周期会让我倾向于使用范围版本。
但是,可读性可能是一个问题;这可以通过使用typedef来帮助,因此迭代器类型更容易管理:
typedef SomeClass::SomeContainer::iterator sc_iter_t;
for (sc_iter_t Iter = m_SomeMemberContainerVar.begin(); Iter != m_SomeMemberContainerVar.end(); ++Iter)
{
}
不是一个巨大的改进,但有点。
我没有任何控制台经验,但在大多数现代C ++编译器中,除了范围问题之外,任一选项最终都是等效的。 visual studio compilier几乎总是在调试代码中将条件比较放在一个隐式临时变量(通常是一个寄存器)中。因此,从逻辑上看,它看起来像是通过每次迭代进行的end()调用,优化的编译代码实际上只进行一次调用,并且比较是在循环中每个子时间完成的唯一事情。
在控制台上可能不是这种情况,但您可以解组循环以检查优化是否正在进行。如果是,那么你可以选择你喜欢的任何风格,或者是你组织中的标准风格。
它可能使对于相互脱节的代码,但是我也想把它拔出来,一个单独的功能,并通过这两个迭代。
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)
- 如果有从中获得不叫端()上每次迭代,我得到它
事情不像:
- 打破了代码
- 开销的其他功能的电话。
但是有一天,我们将有lambda!
我认为这根本不是坏事。只需使用typedef来避免STL详细程度和长行。
typedef set<Apple> AppleSet;
typedef AppleSet::iterator AppleIter;
AppleSet apples;
for (AppleIter it = apples.begin (); it != apples.end (); ++it)
{
...
}
Spartan Programming 是缓解您的风格问题的一种方法。
如果您关注范围,可以在初始化和循环周围抛出大括号。通常我要做的是在函数的开头声明迭代器并在整个程序中重用它们。
我同意Ferruccio。某些人可能会首选这种风格,以便将end()调用拉出循环。
我可能还会补充一点,C ++ 0x实际上会使两个版本更清晰:
for (auto iter = container.begin(); iter != container.end(); ++iter)
{
...
}
auto iter = container.begin();
auto endIter = container.end();
for (; iter != endIter; ++iter)
{
...
}
我通常会写:
SomeClass::SomeContainer::iterator Iter = m_SomeMemberContainerVar.begin(),
IterEnd = m_SomeMemberContainerVar.end();
for(...)
我发现第二个选项更具可读性,因为你最终没有一条巨行。但是,Ferruccio对范围提出了一个很好的观点。