尽量用算法调用代替手写循环
每个算法接受至少一对用来指示将被操作的对象区间的迭代器。比如,min_element可以找出此区间中的最小的值,而accumulate则对区间内的元素作某种形式的整体求和运算(参见条款37),partition将区间内的元素分割为满足和不满足某判决条件的两个部分(参见条款31)。当算法被执行时,它们必须检查指示给它的区间中的每个元素,并且是按你所期望的方式进行的:从区间的起始点循还到结束点。有一些算法,比如find和find_if,可能在遍历完成前就返回了,但即使是这些算法,内部都包含一个循环。毕竟,即使是find和find_if也必须在查看过了每个元素后,才能断定它们所寻找的元素在不在此区间内。
使用算法可以代替我们大部分的循环工作,例如你有一个Widget的容器,想要对每个Widget调用重绘:
class Widget
{
public:
Widget(int value): m_value(value){}
void print()
{
cout <<"print widget :"<<m_value<<endl;
}
private:
int m_value; //value
};
vector<Widget> vw =
{
Widget(1),
Widget(2),
Widget(3)
};
//其实基于范围的for循环代码以及比迭代器和原始的for循环代码优雅很多了
for(auto& widget : vw)
{
widget.print();
}
但是我们确实可以使用算法帮助我们减少一部分工作,例如for_each:
std::for_each(vw.begin(), vw.end(), mem_fn(&Widget::print));
//打印输出如下
print widget :1
print widget :2
print widget :3
事实上,本条款将证明调用算法通常比手写的循环更优越。
为什么?有三个理由:
● 效率:算法通常比程序员产生的循环更高效。
● 正确性:写循环时比调用算法更容易产生错误。
● 可维护性:算法通常使代码比相应的显式循环更干净、更直观
例如给一个容器的所有的值都加上一个数值并保存到另一个容器:
你可能会这么做(或者使用迭代器)
vector<int> vNums = {1,2,3,4,5,6};
deque<int> dNums;
for(auto value : vNums)
{
dNums.push_back(value + 100);
}
但是使用算法我们可以这么做:
//1.
std::transform(vNums.begin(), vNums.end(), std::inserter(dNums, dNums.begin()), std::bind(std::plus<int>(), _1, 100));
//2.
std::transform(vNums.begin(), vNums.end(), std::inserter(dNums, dNums.begin()), [](int l)
{
return l + 40;
});
我们可以基于不同的类似于std::plus<int>()
,做更多的扩展。
使用STL容器的C++精致程序中的循环比不使用STL的等价程序少多了。这是好事。只要能用高层次的术语——如insert、find和for_each,取代了低层次的词汇——如for、while和do,我们就提升了软件的抽象层次,并因此使得它更容易实现、文档化、增强和维护。