第二十一篇:最佳谓词函数 --- 函数对象

前言

       学习C++标准算法时,我们知道带有_if的算法都带有一个谓词函数。此前我们直接使用函数名充当谓词函数,这样做有很大的局限性( 下文会做分析 )。为此本文将介绍C++中一个新的技术:函数对象。用它来充当谓词函数就能解决这个问题。

什么是函数对象?

       函数对象就是重载了函数调用操作符的对象。我们知道,函数调用规则已经非常固定了,那么为何要重载函数调用操作符呢?因为重载后,这个函数调用操作符将不再用作一般意义的函数调用功能。不能调用各种函数的对象那还叫对象吗?从语义上来讲,它不符合对象的设定原则" 对现实事物的虚拟 ",因此我们叫它函数对象以区别于其他对象。那这样的对象有什么用?这个问题稍后讨论,下面来看一个简单的用作求绝对值的函数对象实现:

 1 #include <iostream>
 2 #include <string>
 3 
 4 using namespace std;
 5 
 6 /*
 7  * 1. 函数对象的作用是虚拟函数,因此对它的命名也按照函数命名规范。
 8  * 2. 重载了函数调用操作符,构造函数也不能用了。当然函数对象只需要()这一个" 函数 ",无需定义其他任何
 9  *    函数,包括构造函数。
10 */
11 class absInt {
12 public:
13     int operator() (int val) {
14         return val < 0? -val : val;
15     }
16 };
17 
18 
19 int main()
20 {
21     absInt absObj;
22     int val1 = -1;
23     int val2 = 1;
24 
25     /*
26      * absObj本质是对象,但它虚拟成了一个返回绝对值的函数。
27     */
28     cout << absObj(val1) << endl;
29     cout << absObj(val2) << endl;
30 
31     return 0;
32 }

       运行结果:

       

       仔细阅读代码便能发现,该程序将对象虚拟化为一个函数来用。

为何要定义函数对象?

       要对象就用对象,函数就用函数,为何费那么大劲把对象整成函数?相信有人会这么问。别急,我们先来回顾一个使用标准算法的例子,这个代码返回容器中长度超过6的单词:

 1 #include <iostream>
 2 #include <algorithm>
 3 #include <vector>
 4 #include <string>
 5 
 6 using namespace std;
 7 
 8 bool GT6 (const string &s) {
 9     return s.size() >= 6;
10 }
11 
12 int main()
13 {
14     /*
15      * 构建测试容器
16     */
17     vector<string> words;
18     words.push_back("China");
19     words.push_back("America");
20     words.push_back("England");
21     words.push_back("Japan");
22 
23     // 获取容器中长度大于6的字符串的个数
24     vector<string> :: size_type wc = count_if(words.begin(), words.end(), GT6);
25 
26     // 打印结果
27     cout << wc << endl;
28 
29     return 0;
30 }

       这段代码没有问题,正常地返回了结果:

       

       但是,如果我改变注意了,又想获得长度大于5的字符串,那么我该怎么做?修改函数调用操作符重载函数是可行的办法,但是当函数复杂起来的时候,这会是一个很麻烦的工作。好在改用函数对象充当谓词函数后就能解决这个问题了。

使用函数对象的改进版本

 1 #include <iostream>
 2 #include <algorithm>
 3 #include <vector>
 4 #include <string>
 5 
 6 using namespace std;
 7 
 8 class GT_cls {
 9 public:
10     GT_cls (string::size_type val=0) : bound(val) {};
11     bool operator() (const string &s) {
12         return s.size() >= bound;
13     }
14         
15 private:
16     string::size_type bound;
17 };
18 
19 int main()
20 {
21     /*
22      * 构建测试容器
23     */
24     vector<string> words;
25     words.push_back("China");
26     words.push_back("America");
27     words.push_back("England");
28     words.push_back("Japan");
29 
30     /*
31      * 获取容器中长度大于6的字符串的个数
32      * 如要取长度大于5的,只要将下面语句中的6改成5即可,不用修改重载函数。
33     */
34     vector<string> :: size_type wc = count_if(words.begin(), words.end(), GT_cls(5));
35 
36     // 打印结果
37     cout << wc << endl;
38 
39     return 0;
40 }

       运行结果:

       

       如果要找长度大于8的单词个数?同理,将函数对象参数改成8即可。

说明

       请细细体会上面的代码,分析其执行过程。

posted @ 2017-01-26 23:14  穆晨  阅读(266)  评论(0编辑  收藏  举报