STL算法涉及面很广,这意味着你本该编写循环来完成的任务也可以用STL算法来完成。
例如,有一个Widget类:
class Widget
{
public:
...
void redraw() const;
...
};
当你想在一个包含Widget的list中调用所有成员的Widget对象,可以用一个循环来完成,
std::list<Widget> datas;
...
for (auto iter = datas.begin(); iter != datas.end(); ++iter)
{
iter->redraw();
}
你也可以选择使用for_each算法来完成:
for_each(datas.begin(), datas.end(), mem_fun_ref(Widget::redraw));
编写一个循环比调用一个算法更在简单,但事实上调用算法通常是更好的选择,它往往优先于任何一个手写循环。理由有三:
- 效率:算法通常比自己学的循环效率更高。
- 正确性:手学循环比使用算法更容器出错。
- 可维护性:使用算法的代码通常比手写循环的更加简洁明了。
从效率看:
使用算法可以减少冗余的计算。
对于循环:
for (auto iter = datas.begin(); iter != datas.end(); ++iter)
{
iter->redraw();
}
每一次循环的额时候,list::end()都要被调用。但是并不需要多次调用它,因为并没有改变list。
如果使用算法:
std::for_each(datas.begin(), datas.end(), mem_fun_ref(&Widget::redraw()));
STL的实现者很清楚,begin、end都是被频繁调用的函数,所以他们尽可能提高其效率,几乎可以肯定会使用inline来编译这些函数,并且努力改善这些函数的代码,尽可能让大多数编译器都能够将循环中的计算提到外面来,避免重复计算。
类库实现者可以根据他们对容器实现的了解程度对遍历过程进行优化,这是库的使用者难以做到的。无论如何实现者肯定比调用者更了解内部的实现细节,他们可以在算法实现中充分利用这些知识。如果你避开算法使用自己的手写循环,你也就放弃了这些算法实现者可能提供的优化手段。
除了一些不太重要的算法之外,其他几乎所有的算法都使用了复杂的计算机科学算法,有些非常复杂,并非一般的程序员所能够达到。
从正确性看:
当你编写循环代码的时候,最紧要的莫过于与要保证你所使用的迭代器都是有效的,并且指向你所希望的地方。
从清晰度看:
最好的代码时最简洁、可读性最好的软件,是最容器扩展功能、最容易维护和适用于新环境的软件,尽管我们对循环更为熟悉,但是从长远上看,算法更具竞争力。
当你看到for、while或do的时候,你所知道的只不过是某一种循环将要出现。要想知道这个循环的用途,哪怕时最初略的用途,你都必须检查具体的代码。但是,当你看到了一个算法调用的时候,它的名称就会指出它的用途。当然,如果你想知道它到底做了什么,你必须检查哪些传给算法的实参的意义,但这通常比看懂一个循环结果的意图要简单得多。
在算法调用与手写循环中的斗争中,关于代码清晰度的底线最终取决于你要在循环中做什么事情。如果你要做的工作与一个算法所实现的功能很相近,那么用算法调用更好。如果你的循环很简单,若使用算法来实现的话,却要求混合使用绑定器或者一个单独的函数子类,那么,手写循环可能更好。最后,如果你在循环中要做的事情很多,而且很复杂,则最好使用算法调用,因为冗长又复杂的计算任务总是应该被放到单独的函数中。而一旦你把循环体移动到单独的函数中,你总是可以找到一个函数传给算法(例如for_each),这样得到的代码有直接、又清楚。
使用STL的精巧的C++程序比不用STL的程序所包含的循环要少得多,这是一件好事。任何时候我们都应该尽量用较高层次的insert、find和for_each来替换较低层次的for、while和do。因为这样可以提高软件的抽象层次,从而使软件更易于编写,更易于文档化,也更易于扩展和维护。