zoukankan      html  css  js  c++  java
  • C++ 仿函数

    先考虑一个简单的例子:假设有一个vector<string>,你的任务是统计长度小于5的string的个数,如果使用count_if函数的话,你的代码可能长成这样:

    1 bool LengthIsLessThanFive(const string& str) {
    2      return str.length()<5;    
    3 }
    4 int res=count_if(vec.begin(), vec.end(), LengthIsLessThanFive);

      其中count_if函数的第三个参数是一个函数指针,返回一个bool类型的值。一般的,如果需要将特定的阈值长度也传入的话,我们可能将函数写成这样:

    1 bool LenthIsLessThan(const string& str, int len) {
    2     return str.length()<len;
    3 }

      这个函数看起来比前面一个版本更具有一般性,但是他不能满足count_if函数的参数要求:count_if要求的是unary function(仅带有一个参数)作为它的最后一个参数。所以问题来了,怎么样找到以上两个函数的一个折中的解决方案呢?

      这个问题其实可以归结于一个data flow的问题,要设计这样一个函数,使其能够access这个特定的length值,回顾我们已有的知识,有三种解决方案可以考虑:

    1、函数的局部变量;

          局部变量不能在函数调用中传递,而且caller无法访问。

    2、函数的参数;

      这种方法我们已经讨论过了,多个参数不适用于count_if函数。

    3、全局变量;

      我们可以将长度阈值设置成一个全局变量,代码可能像这样:

    1 int maxLength;
    2 bool LengthIsLessThan(const string& str) {
    3     return str.length()<maxLength;
    4 }
    5 int res=count_if(vec.begiin(), vec.end(), LengthIsLessThan);

      这段代码看似很不错,实则不符合规范,刚重要的是,它不优雅。原因有以下几点要考虑:

    1、容易出错;

      为什么这么说呢,我们必须先初始化maxLength的值,才能继续接下来的工作,如果我们忘了,则可能无法得到正确答案。此外,变量maxLength和函数LengthIsLessThan之间是没有必然联系的,编译器无法确定在调用该函数前是否将变量初始化,给码农平添负担。

    2、没有可扩展性;

      如果我们每遇到一个类似的问题就新建一个全局变量,尤其是多人合作写代码时,很容易引起命名空间污染(namespace polution)的问题;当范围域内有多个变量时,我们用到的可能不是我们想要的那个。

    3、全局变量的问题;

      每当新建一个全局变量,即使是为了coding的便利,我们也要知道我们应该尽可能的少使用全局变量,因为它的cost很高;而且可能暗示你这里有一些待解决的优化方案。

      说了这么多,还是要回到我们原始的那个问题,有什么解决方案呢?答案当然就是这篇blog的正题部分:仿函数。

      我们的初衷是想设计一个unary function,使其能做binary function的工作,这看起来并不容易,但是仿函数能解决这个问题。

      先来看仿函数的通俗定义:仿函数(functor)又称为函数对象(function object)是一个能行使函数功能的类。仿函数的语法几乎和我们普通的函数调用一样,不过作为仿函数的类,都必须重载operator()运算符,举个例子:

    1 class Func{
    2     public:
    3         void operator() (const string& str) const {
    4             cout<<str<<endl;
    5         }
    6 };
    1 Func myFunc;
    2 myFunc("helloworld!");
    >>>helloworld!

      仿函数其实是上述解决方案中的第四种方案:成员变量。成员函数可以很自然的访问成员变量:

      

    复制代码
     1 class StringAppend{
     2     public:
     3         explicit StringAppend(const string& str) : ss(str){}
     4 
     5         void operator() (const string& str) const{
     6              cout<<str<<' '<<ss<<endl;
     7         }
     8     
     9     private:
    10         const string ss;  
    11 };
    12 
    13 StringAppend myFunc("is world");
    14 myFunc("hello");
    >>>hellois world
    复制代码

      我相信这个例子能让你体会到一点点仿函数的作用了;它既能想普通函数一样传入给定数量的参数,还能存储或者处理更多我们需要的有用信息。

      让我们回到count_if的问题中去,是不是觉得问题变得豁然开朗了?

    复制代码
    1 class ShorterThan {
    2     public:
    3         explicit ShorterThan(int maxLength) : length(maxLength) {}
    4         bool operator() (const string& str) const {
    5             return str.length() < length;
    6         }
    7     private:
    8         const int length;
    9 };
    复制代码
    1 count_if(myVector.begin(), myVector.end(), ShorterThan(length));//直接调用即可

      这里需要注意的是,不要纠结于语法问题:ShorterThan(length)似乎并没有调用operator()函数?其实它调用了,创建了一个临时对象。你也可以自己加一些输出语句看一看。

      这篇博文就先记到这里了,仿函数也在STL中大量涉及到,不彻底弄懂仿函数的问题看到STL源码就会一头包。

  • 相关阅读:
    JS BOM对象 History对象 Location对象
    JS 字符串对象 数组对象 函数对象 函数作用域
    JS 引入方式 基本数据类型 运算符 控制语句 循环 异常
    Pycharm Html CSS JS 快捷方式创建元素
    CSS 内外边距 float positio属性
    CSS 颜色 字体 背景 文本 边框 列表 display属性
    【Android】RxJava的使用(三)转换——map、flatMap
    【Android】RxJava的使用(二)Action
    【Android】RxJava的使用(一)基本用法
    【Android】Retrofit 2.0 的使用
  • 原文地址:https://www.cnblogs.com/lvchaoshun/p/8536866.html
Copyright © 2011-2022 走看看