zoukankan      html  css  js  c++  java
  • 第二十一篇:最佳谓词函数 --- 函数对象

    前言

           学习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即可。

    说明

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

  • 相关阅读:
    POJ 3261 Milk Patterns (求可重叠的k次最长重复子串)
    UVaLive 5031 Graph and Queries (Treap)
    Uva 11996 Jewel Magic (Splay)
    HYSBZ
    POJ 3580 SuperMemo (Splay 区间更新、翻转、循环右移,插入,删除,查询)
    HDU 1890 Robotic Sort (Splay 区间翻转)
    【转】ACM中java的使用
    HDU 4267 A Simple Problem with Integers (树状数组)
    POJ 1195 Mobile phones (二维树状数组)
    HDU 4417 Super Mario (树状数组/线段树)
  • 原文地址:https://www.cnblogs.com/muchen/p/6352211.html
Copyright © 2011-2022 走看看