• C++11新特性之八——函数对象function


    详细请看《C++ Primer plus》(第六版中文版)

    http://www.cnblogs.com/lvpengms/archive/2011/02/21/1960078.html

    备注:

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

    • 首先,因为对象可以在内部修改而不用改动外部接口,因此设计更灵活,更富有弹性函数对象也具备有存储先前调用结果的数据成员。在使用普通函数时需要将先前调用的结果存储在全程或者本地静态变量中,但是全程或者本地静态变量有某些我们不愿意看到的缺陷。
    • 其次,在函数对象中编译器能实现内联调用,从而更进一步增强了性能。这在函数指针中几乎是不可能实现的。
    • C++11还提供了limbda表达式来实现函数的灵活调用。详见《C++ Primer Plus》第18章。

    ----------------------------------------------------------------------------------------------------------------------------------------------------------

     std::function是一组函数对象包装类的模板,实现了一个泛型的回调机制。function与函数指针比较相似,优点在于它允许用户在目标的实现上拥有更大的弹性,即目标既可以是普通函数,也可以是函数对象和类的成员函数,而且可以给函数添加状态。
    声明一个function时,需要给出所包装的函数对象的返回值类型和各个参数的类型。比如,声明一个function,它返回一个bool类型并接受一个int类型和一个float类型的参数,可以像下面这样:
    function<bool (int, float)> f;

    下面简要介绍一下function的比较重要的几个接口。

    --------------------------------------------------------------------------------

    function();
    缺省构造函数,创建一个空的函数对象。如果一个空的function被调用,将会抛出一个类型为bad_function_call的异常。
    --------------------------------------------------------------------------------
    template <typename F> function(F g);

    这个泛型的构造函数接受一个兼容的函数对象,即这样一个函数或函数对象,它的返回类型与被构造的function的返回类型或者一样,或者可以隐式转换,并且它的参数也要与被构造的function的参数类型或者一样,或者可以隐式转换。

    注意,也可以使用另外一个function实例来进行构造。这样做,并且function g为空,则被构造的function也为空。使用空的函数指针和空的成员函数指针也会产生空的function。如果这样做,并且function g为空,则被构造的function也为空。使用空的函数指针和空的成员函数指针也会产生空的function。

    --------------------------------------------------------------------------------

    template <typename F> function(reference_wrapper<F> g);
    这个构造函数与前一个类似,但它接受的函数对象包装在一个reference_wrapper中,用以避免通过值来传递而产生函数或函数对象的一份拷贝。这同样要求函数对象兼容于function的签名。
    --------------------------------------------------------------------------------
    function& operator=(const function& g);
    赋值操作符保存g中的函数或函数对象的一份拷贝;如果g为空,被赋值的函数也将为空。
    --------------------------------------------------------------------------------
    template<typename F> function& operator=(F g);
    这个泛型赋值操作符接受一个兼容的函数指针或函数对象。
    注意,也可以用另一个 function 实例(带有不同但兼容的签名)来赋值。这同样意味着,如果g是另一个function实例且为空,则赋值后的函数也为空。赋值一个空的函数指针或空的成员函数指针也会使function为空。
    --------------------------------------------------------------------------------
    bool empty() const;
    这个成员函数返回一个布尔值,表示该function是否含有一个函数或函数对象。如果有一个目标函数或函数对象可被调用,它返回 false 。因为一个function可以在一个布尔上下文中测试,或者与0进行比较,因此这个成员函数可能会在未来版本的库中被取消,你应该避免使用它。
    --------------------------------------------------------------------------------
    void clear();
    这个成员函数清除 function, 即它不再关联到一个函数或函数对象。如果function已经是空的,这个调用没有影响。在调用后,function肯定为空。令一个function为空的首选方法是赋0给它;clear 可能在未来版本的库中被取消。
    --------------------------------------------------------------------------------
    result_type operator()(Arg1 a1, Arg2 a2, ..., ArgN aN) const;
    调用操作符是调用function的方法。你不能调用一个空的 function ,那样会抛出一个bad_function_call的异常。调用操作符的执行会调用function中的函数或函数对象,并返回它的结果。
    --------------------------------------------------------------------------------
    下面分别给出使用function来包装普通函数,函数对象和类的成员函数的参考代码。
    1、普通函数
    1 int Add(int x, int y)
    2 
    3 {
    4    return x+y;
    5 }
    6 function<int (int,int)> f = Add;
    7 int z = f(2, 3);

    2、函数对象

     1 class CStudent
     2 {
     3 public:
     4             void operator() (string strName, int nAge)
     5             {
     6                 cout << strName << " : " << nAge << endl; 
     7             }
     8 };
     9 
    10 CStudent stu;
    11 function<void (string, int)> f = stu;
    12 f("Mike",  12);

    3、类的成员函数

     1 struct TAdd
     2 {
     3     int Add(int x,int y)
     4     {
     5         return x+y;
     6     }
     7 };
     8 
     9 function<int  (TAdd *, int, int)> f = TAdd::Add;
    10 TAdd tAdd;
    11 f(&tAdd, 2, 3);   // 如果前面的模板参数为传值或引用,直接传入tAdd即可

    接下来我们来看看使用function来保存函数对象状态的情况。考虑下面的代码:

     1 class CAdd
     2 {
     3 public:
     4     CAdd():m_nSum(0) { NULL; }
     5     int operator()(int i)  //重载 () 运算符
     6     {
     7           m_nSum += i;
     8           return m_nSum;
     9     }
    10 
    11     int Sum() const 
    12     {
    13         return m_nSum;
    14     }
    15 
    16 private:
    17     int m_nSum;
    18 };
    19 
    20 int main() 
    21 {
    22     CAdd add;
    23     function<int (int)> f1 = add;
    24     function<int (int)> f2 = add;
    25     cout << f1(10) << "," << f2(10) << "," << add.Sum() << endl;
    26     return 0;
    27 }
    可能和大家想象的结果不一样,上面程序的输出是:10,10,0。我们将同一个函数对象赋值给了两个function,然后分别调用了这两个function,但函数对象中m_nSum的状态并没有被保持,问题出在哪儿呢?这是因为function的缺省行为是拷贝一份传递给它的函数对象,于是f1和f2中保存的都是add对象的拷贝,调用f1和f2后,add对象中的值并没有被修改。

    C++ 11中提供了ref和cref函数,来提供对象的引用和常引用的包装。要使function能够正确地保存函数对象的状态,我们可以这样来修改代码:

    1 CAdd add;
    2 function<int(int)> f1 = ref(add);
    3 function<int(int)> f2 = ref(add);
    另外,在两个function之间赋值时,如果源function保存的是函数对象的拷贝,则目标function保存的也是函数对象的拷贝;如果源function保存的是函数对象的引用,则目标function保存的也是函数对象的引用。
  • 相关阅读:
    兼容Android 和 ios JavaScript copy paste
    hello-循环神经网络(RNN)原理
    236. Lowest Common Ancestor of a Binary Tree
    Tensorflow 之模型内容可视化
    CUDA,cudnn一些常见版本问题
    MongoDB国内学术研究(部分)
    Unsupported major.minor version 52.0
    WebService的两种方式SOAP和REST比较
    廖雪峰老师的git在线教程
    MAVEN ERROR : Dynamic Web Module 3.0 requires Java 1.6 or newer
  • 原文地址:https://www.cnblogs.com/yyxt/p/3987717.html
Copyright © 2020-2023  润新知