zoukankan      html  css  js  c++  java
  • 函数对象

    尽管函数指针被广泛用于实现函数回调,但C++还提供了一个重要的实现回调函数的方法,那就是函数对象。函数对象(也称“算符”)是重载了“()”操作符的普通类对象。因此从语法上讲,函数对象与普通的函数行为类似。

     
    用函数对象代替函数指针有几个优点,首先,因为对象可以在内部修改而不用改动外部接口,因此设计更灵活,更富有弹性。函数对象也具备有存储先前调用结果的数据成员。在使用普通函数时需要将先前调用的结果存储在全程或者本地静态变量中,但是全程或者本地静态变量有某些我们不愿意看到的缺陷。
    其次,在函数对象中编译器能实现内联调用,从而更进一步增强了性能。这在函数指针中几乎是不可能实现的。
     
    下面举例说明如何定义和使用函数对象。首先,声明一个普通的类并重载“()”操作符:
     
    class Negate
    {
    public:
    int operator() (int n) { return -n;}
    };
     
    重载操作语句中,记住第一个圆括弧总是空的,因为它代表重载的操作符名;第二个圆括弧是参数列表。一般在重载操作符时,参数数量是固定的,而重载“()”操作符时有所不同,它可以有任意多个参数。
     
    因为在Negate中内建的操作是一元的(只有一个操作数),重载的“()”操作符也只有一个参数。返回类型与参数类型相同-本例中为int。函数返回与参数符号相反的整数。
     
    使用函数对象
     
    我们现在定义一个叫Callback()的函数来测试函数对象。Callback()有两个参数:一个为int一个是对类Negate的引用。Callback()将函数对象neg作为一个普通的函数名:
     
    #include
    using std::cout;
     
    void Callback(int n, Negate & neg)
    {
    int val = neg(n); //调用重载的操作符“()”
    cout << val;
    }
     
    不要的代码中,注意neg是对象,而不是函数。编译器将语句
     
    int val = neg(n);
     
    转化为
     
    int val = neg.operator()(n);
     
    通常,函数对象不定义构造函数和析构函数。因此,在创建和销毁过程中就不会发生任何问题。前面曾提到过,编译器能内联重载的操作符代码,所以就避免了与函数调用相关的运行时问题。
     
    为了完成上面个例子,我们用主函数main()实现Callback()的参数传递:
     
    int main()
    {
    Callback(5, Negate() ); //输出 -5
    }
     
    本例传递整数5和一个临时Negate对象到Callback(),然后程序输出-5。
     
    模板函数对象
     
    从上面的例子中可以看出,其数据类型被限制在int,而通用性是函数对象的优势之一,如何创建具有通用性的函数对象呢?方法是使用模板,也就是将重载的操作符“()”定义为类成员模板,以便函数对象适用于任何数据类型:如double,_int64或char:
     
    class GenericNegate
    {
    public:
    template T operator() (T t) const {return -t;}
    };
     
    int main()
    {
    GenericNegate negate;
    cout<< negate(5.3333); // double
    cout<< negate(10000000000i64); // __int64
    }
     
    如果用普通的回调函数实现上述的灵活性是相当困难的。
     
    标准库中函数对象
     
    C++标准库定义了几个有用的函数对象,它们可以被放到STL算法中。例如,sort()算法以判断对象(predicate object)作为其第三个参数。判断对象是一个返回Boolean型结果的模板化的函数对象。可以向sort()传递greater<>或者less<>来强行实现排序的升序或降序:
     
    #include // for greater<> and less<>
    #include //for sort()
    #include
    using namespace std;
     
    int main()
    {
    vector vi;
    //..填充向量
    sort(vi.begin(), vi.end(), greater() );//降序( descending )
    sort(vi.begin(), vi.end(), less() ); //升序 ( ascending )
    }
     
    转自:http://blog.chinaunix.net/u2/64540/showart_707470.html 
     

    算术函数对象

    预定义的算术函数对象支持加减乘除求余和取反调用的操作符是与Type 相
    关联的实例对一个class 类型如果它提供了该操作符的重载实例则调用该实例
    加法plus<Types>
    plus<string> stringAdd;
    // 调用 string::operator+()
    sres = stringAdd( sval1, sval2 );
    dres = BinaryFunc( plus<double>(), dval1, dval2 );
    减法minus<Type>
    minus<int> intSub;
    ires = intSub( ival1, ival2 );
    dres = BinaryFunc( minus<double>(), dval1, dval2 );
    乘法multiplies<Type>
    multiplies<complex> complexMultiplies;
    cres = complexMultiplies( cval1, cval2 );
    dres = BinaryFunc( multiplies<double>(), dval1, dval2 );
    除法divides<Type>
    divides<int> intDivides;
    ires = intDivides( ival1, ival2 );
    dres = BinaryFunc( divides<double>(), dval1, dval2 );
    求余modulus<Type>
    modulus<int> IntModulus;
    Ires = IntModulus( Ival1, Ival2 );
    ires = BinaryFunc( modulus<int>(), ival2, ival1 );
    取反negate<Type>
    negate<int> intNegate;
    ires = intNegate( ires );
    Ires = UnaryFunc( negate<int>(), Ival1 );
     

    关系函数对象

    预定义的关系函数对象支持等于不等于大于大于等于小于和小于等于
    等于equal_to<Type>
    equal_to<string> stringEqual;
    sres = stringEqual( sval1, sval2 );
    ires = count_if( svec.begin(), svec.end(),
    equal_to<string>(), sval1 );
    不等于not_equal_to<Type>
    not_equal_to<complex> complexNotEqual;
    cres = complexNotEqual( cval1, cval2 );
    ires = count_if( svec.begin(), svec.end(),
    not_equal_to<string>(), sval1 );
    大于greater<Type>
    greater<int> intGreater;
    ires = intGreater( ival1, ival2 );
    ires = count_if( svec.begin(), svec.end(),
    greater<string>(), sval1 );
    大于等于greater_equal<Type>
    greater_equal<double> doubleGreaterEqual;
    dres = doubleGreaterEqual( dval1, dval2 );
    ires = count_if( svec.begin(), svec.end(),
    greater_equal<string>(), sval1 );
    小于less<Type>
    less<int> IntLess;
    Ires = IntLess( Ival1, Ival2 );
    ires = count_if( svec.begin(), svec.end(),
    less<string>(), sval1 );
    小于等于less_equal<Type>
    less_equal<int> intLessEqual;
    ires = intLessEqual( ival1, ival2 );
    ires = count_if( svec.begin(), svec.end(),
    less_equal<string>(), sval1 );
     

    逻辑函数对象

    预定义的逻辑函数对象支持逻辑与两个操作数都为true 时结果值为true——应用与
    Type 相关联的&& 逻辑或两个操作数中有一个为true 返回true——应用与Type 相关联
    的|| 和逻辑非操作数为false 则返回true——应用与Type 相关联的!操作符
    逻辑与logical_and<Type>
    logical_and<int> intAnd;
    ires = intAnd( ival1, ival2 );
    dres = BinaryFunc( logical_and<double>(), dval1, dval2 );
    逻辑或logical_or<Type>
    logical_or<int> intSub;
    ires = intSub( ival1, ival2 );
    dres = BinaryFunc( logical_or<double>(), dval1, dval2 );
    逻辑非 logical_not<Type>
    logical_not<int> IntNot;
    Ires = IntNot( Ival1, Ival2 );
    dres = UnaryFunc( logical_not<double>(), dval1 );
     

    函数对象的函数适配器

    标准库还提供了一组函数适配器用来特殊化或者扩展一元和二元函数对象适配器是
    一种特殊的类它被分成下面两类:
    1 绑定器binder ( binder ) 通过把二元函数对象的一个实参绑定到一个特殊的值上
    将其转换成一元函数对象例如为了计数一个容器中小于或等于10 的元素的个数我们可能会向count_if()传递一个less_equal 函数对象以及一个被绑定为10 的实参。
    2 取反器negator ( negator ) 是一个将函数对象的值翻转的函数适配器例如为了
    计数一个容器中所有大于10 的元素的个数我们可以向count_if()传递less_equal 函数对象的
    negator 该函数对象有一个实参被绑定为10 当然在这种情况下直接传递greater 对象
    的binder 并把一个实参绑定为10 更为简洁明了。
    C++标准库提供了两种预定义的binder 适配器bind1st 和bind2nd 正如你所预料的
    bind1st 把值绑定到二元函数对象的第一个实参上bind2nd 把值绑定在第二个实参上例如
    为了计数容器中所有小于或等于10 的元素的个数我们可以这样向count_if()传递
    count_if( vec.begin(), vec.end(),
    bind2nd( less_equal<int>(), 10 ));
    标准库提供了两个预定义的negator 适配器not1 和not2 同样正如你所料想的not1
    翻转一元预定义函数对象的真值而not2 翻转二元谓词函数的真值为了取反less_equal 函
    数对象的绑定我们可以这样写
    count_if( vec.begin(), vec.end(),
    not1( bind2nd( less_equal<int>(), 10 )));

    实现函数对象

    函数对象类定义的最简单形式包含一个被重载的函数调用操作符例如下面是一个二
    元函数对象它判定一个值是否小于等于10
    // 函数对象类的简单形式
    class less_equal_ten {
    public:
    bool operator() ( int val )
    { return val <= 10; }
    };
    使用这个对象的方式与使用预定义函数对象的方式相同例如下面是修改后的count_if()
    调用它使用了我们的函数对象
    count_if( vec.begin(), vec.end(), less_equal_ten() );
    毫无疑问这个类是相当局限的我们可以应用一个negator 来计数容器中大于10 的元
    素的个数
    count_if( vec.begin(), vec.end(),
    not1(less_equal_ten()) );
    我们也可以通过允许用户提供一个与每个元素比较的值来扩展我们的实现一种做法是
    引入一个数据成员来存储被比较的值以及一个构造函数把这个成员初始化为用户指定的值
    class less_equal_value {
    public:
    less_equal_value( int val ) : _val( val ) {}
    bool operator() ( int val ) { return val <= _val; }
    private:
    int _val;
    };
    我们现在用这个对象指定一个任意的整数值例如下面的调用计数小于等于25 的元素
    的个数
    count_if( vec.begin(), vec.end(), less_equal_value( 25 ));
    另外一种类的实现方式不使用构造函数它根据被比较的值对类参数化例如
    template < int _val >
    class less_equal_value {
    public:
    bool operator() ( int val ) { return val <= _val; }
    };
    下面给出了怎样调用这个类来计数小于等于25 的元素的个数
    count_if( vec.begin(), vec.end(), less_equal_value<25>() );
  • 相关阅读:
    解决Linux中Too many open files问题
    10个必需的iOS开发工具和资源
    @ModelAttribute跟@SessionAttributes的应用
    7个改变世界的Java项目
    Java序列化与反序列化
    微软推出“IE9梦幻任务栏Pin计划”
    java中replace和replaceAll的区别
    【转】hibernate中annotation方式SchemaExport无法生成表的原因(ORA02261)
    permgen space报错
    em和px
  • 原文地址:https://www.cnblogs.com/yzl050819/p/6762552.html
Copyright © 2011-2022 走看看