我有一个对象矢量:
std::vector<Object> data;
现在我需要在数据数组中调用Object::Foo()
:
for (int i=0; i < data.size(); ++i) data[i].Foo(); // I think, the most slower
for (Object *it : data) it->Foo(); // And these are equal?
for (auto it=data.begin(); i!=data.end(); ++it) it->Foo();
我知道这些方法,但现在我正在学习STL,我在那里找到了for_each
:
for_each(data.begin(), data.end(), mem_fun(&Object::Foo));
哪一个更好?换句话说,前两个例子更“基础”,并且很容易理解普通开发人员的构造。
STL算法的速度如何(在当前情况下for_each
和mem_fun
的速度如何)。我真的需要学习吗?
答案 0 :(得分:5)
使用合适的编译器时,不会有明显的性能差异。
for (int i=0; i < data.size(); ++i)
data[i].Foo();
不慢,只是简介!简单的代码往往更快。在这种情况下,所有版本都同样简单
for (Object& it : data)
it.Foo();
修复了您的问题:使用引用来避免不必要的复制(!)。此外,这大致相当于:
{
auto curr = std::begin(data);
auto endit = std::end(data);
for (; curr != endit; ++curr)
curr.Foo();
}
关于mem_fun
/ mem_fun_ref
及相关(bind_1st
,negate
等),我建议你不要需要来学习它们。阅读它们可能是有意义的,但C ++ 11以更优雅的方式解决了这些“问题”。
// using std::bind
for_each(data.begin(), data.end(), std::bind(&Object::Foo, _1));
// using lambdas
for_each(data.begin(), data.end(), [] (Object& o) { o.Foo(); });
事实上,即使在C ++ 03中,图书馆(如Loki,Boost Bind,Boost Lambda和最近的Boost Phoenix也可以使这一点同样简洁。
答案 1 :(得分:2)
哪一个更好?
使用std::for_each()
更好,因为它可以更容易地避免像搞乱迭代器这样的愚蠢错误(使用两个引用不同的序列)。但是std::mem_func()
语法笨拙且难以理解,并且以这种方式组成更复杂的循环体不可避免地导致非常难以理解的代码。
这是通过引入lambda函数的C ++ 11解决的:
std::for_each( data.begin(), data.end(), [](Object& obj){obj.Foo();} );
对我而言,这显然是胜利者。它易于阅读,并且可以在混合使用不同的迭代器时轻松识别。
STL算法的速度如何(在当前情况下
for_each
和mem_fun
的速度如何)。
很难说,取决于实现,以及编译器的优化能力。 如果这个循环非常重要,可以在一个重要的位置进行分析,找出并抛弃可读性。在那之前,可读性胜过所有其他问题。
答案 2 :(得分:1)
最好的代码是您没有编写的代码。
因此,使用STL代码较少,紧凑且具有逻辑系统。
但更重要的是,你保留一种风格而不改变它。我个人使用STL,从来没有遇到过问题。
大多数代码示例都是用STL编写的,如果你使用它,你就会理解样本。
答案 3 :(得分:1)
尽管在std :: vector上优化std :: for_each()的可能性不大,但它仍然可能比手动循环更快(包括使用基于范围的版本)。对于其他容器和/或其他算法,潜在的好处更大。
另一个重要方面是你通常想说你正在做什么而不是如何这样做:除了实施有更好的解决方法这个问题,它使事情更具可读性。此外,似乎错误率取决于使用更多代码具有更多错误而编写的代码量。这意味着更少的代码具有更少的错误。
答案 4 :(得分:0)
你可以最可靠地做到没有错误的那个,而其他人看起来最容易理解的更好。
从长远来看,你知道迭代器是如何工作的,以及mem_fun
之类的工作方式是很好的。如果集合对象发生更改,使用迭代器还可以使用相同的代码。