STL算法之回调函数和函数对象的理解及设计

来源:互联网 发布:数据库标准规范 编辑:程序博客网 时间:2024/06/18 17:03
引言:在我们使用STL的算法的时候,很多算法提供回调函数为参数和函数对象来作为参数,提供
更加强大的功能。就比如STL中sort算法来说吧,我们可能一般情况下这么使用sort(v.begin(), v.end());
这种情况下是对容器中元素默认升序排序。那么我们怎么实现降序排序呢?有两种方法,要么用库中提
供的函数对象作为参数,要么自己编写个函数或者函数对象来作为参数。那么问题来了,库中提供的
函数对象怎么用呢?是啥意思呢?即使我们传进去了也不知道为啥这样?传个自己写的函数?那自己
该怎么写呢?怎么写才能符合STL要求的接口呢?所有的这一切其实都可以通过调试代码单步来理解的。
我们没必要专门研究《STL源码剖析》之类的高深书籍,照样也可以看到看懂STL源码的实现。调试的
时候单步跟进去便揭开了庐山正面目,其实里面的代码也挺好理解的,只不过都是用模板写的,看起来
不那么舒服,多看几眼就懂了。STL真是GP程序设计思想啊,一切皆模板.。。。
           接下来通过使用一个STL的一个accumulate算法来解答上面的所有困惑:

     首先贴一段完整的代码,看看读者能否看懂每一句代码是怎么运行的,如果不懂看后面的解释:
  1. //以下代码来自:http://www.cplusplus.com/  
  2. // accumulate example  
  3. #include <iostream>  
  4. #include <functional>  
  5. #include <numeric>  
  6. using namespace std;  
  7.   
  8. int myfunction (int x, int y) {return x+2*y;}  
  9.   
  10. struct myclass {  
  11.  int operator()(int x, int y) {return x+3*y;}  
  12. } myobject;  
  13.   
  14. int main () {  
  15.   int init = 100;  
  16.   int numbers[] = {10,20,30};  
  17.   
  18.   cout << "using default accumulate: ";  
  19.   cout << accumulate(numbers,numbers+3,init);  
  20.   cout << endl;  
  21.   
  22.   cout << "using functional's minus: ";  
  23.   cout << accumulate (numbers, numbers+3, init, minus<int>() );  
  24.   cout << endl;  
  25.   
  26.   cout << "using custom function: ";  
  27.   cout << accumulate (numbers, numbers+3, init, myfunction );  
  28.   cout << endl;  
  29.   
  30.   cout << "using custom object: ";  
  31.   cout << accumulate (numbers, numbers+3, init, myobject );  
  32.   cout << endl;  
  33.   
  34.   return 0;  
  35. }  
        在上面代码中accumulate(numbers,numbers+3,init)这句调用就不解释了,最基本的使用,相信只要接触了STL的都懂。
可能让STL初学者迷惑的是后面三次的调用。接下来逐个分析下:
    一. 解析:accumulate (numbers, numbers+3, init, minus<int>() );
   猛一看,这句代码不好理解。。。于是查询MSDN或者到C++网站查询使用方法,查看半天结果全英文,最终还是不懂。。。
  没看懂该怎么办呢?没错,开始单步调试,跳进这句代码的调用,豁然开朗:
      没错,我们很幸运地看到了accumulate算法的实现,那么这段代码还看不懂么???稍微懂点模板的很容易看懂了,就一个简单的for
语句迭代累加。。。似乎也没那么神奇!现在我们可以看到我们传的那个函数对象参数就是__binary_op,函数指针的调用方式就不用说
了吧--__init = __binary_op(__init, *__first);这句代码便是对我们的函数的调用,可见第一个参数使我们传进去的init,第二个是STL容器
中的元素。那么我们现在关心的是那个函数对象是怎么实现的,对吧?于是继续跳进__binary_op函数的调用:

     到了这里上面的所有疑惑估计都豁然开朗了吧!!!我们调用的minus<int>()这个函数对象其实实现的功能是返回x - y。
那么accumulate (numbers, numbers+3, init, minus<int>() )这句代码实现的功能也就迎刃而解了,它实现的功能就是
init = init - Elem; 这个表达式的累加。Elem这里代表容器的每个元素。那么最终结果就是60了。
     二. 解析:accumulate (numbers, numbers+3, init, myfunction );
    这句代码和上面的类似,只不过第三个参数是自己定义的一个回调函数,通过自定义来满足自己的功能需求。那么初学者
的疑惑就来了,该怎么自定义这个回调函数呢?其实不难,从上面的调试中我们就看到了accumulate的实现了,它的第
三个参数__binary_op是一个含有两个参数的回调函数--__binary_op(__init, *__first);那么我们只要定义一个含有两个参数
的回调函数即可,当然也要有返回值。这段代码中是这么定义的:
  1. int myfunction (int x, int y) {return x+2*y;}  
很明显,这个函数是否和要求的,那么我们就这么写了!!!这下会写自己的回调函数了吧,就这么简单。
     三.解析:accumulate (numbers, numbers+3, init, myobject );
   这句代码也和上面的完全类似,只不过第三个参数是自己定义的函数对象,定义方法和自定义回调函数一样的,参数和
库中的接口一致即可。给初学者稍微解释下函数对象--函数对象就一个类重载了()运算符,然后就可以用对象名+参数
列表来调用这个重载函数了,这不就极其类似一个函数的调用吗?对,就是这样的,所以称这种对象为函数对象。
这个理解是我自己这么理解,仅供参考!

      结语:
          到此为止,相信大家对STL算法中回调函数和函数对象有所理解了吧,最起码能看懂别人或者示例代码了吧,也最
起码学会了如何定制自己的回调函数或者函数对象来使用STL的算法了吧。
0 0
原创粉丝点击