详细请看《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、普通函数
 int Add(int x, int y)

 {
return x+y;
}
function<int (int,int)> f = Add;
int z = f(, );

2、函数对象

  class CStudent
{
public:
void operator() (string strName, int nAge)
{
cout << strName << " : " << nAge << endl;
}
}; CStudent stu;
function<void (string, int)> f = stu;
f("Mike", );

3、类的成员函数

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

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

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

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

 CAdd add;
function<int(int)> f1 = ref(add);
function<int(int)> f2 = ref(add);
另外,在两个function之间赋值时,如果源function保存的是函数对象的拷贝,则目标function保存的也是函数对象的拷贝;如果源function保存的是函数对象的引用,则目标function保存的也是函数对象的引用。

C++11新特性之八——函数对象function的更多相关文章

  1. C++ 11 新特性:函数声明auto

    1.概览 1.1 函数名中的箭头,用来表明函数的return type,其使用在函数的返回类型需要通过模板参数进行推导,使用在decltype()和declval()不方便的场景 2.正文 c++ 中 ...

  2. 【C++11】新特性——Lambda函数

    本篇文章由:http://www.sollyu.com/c11-new-lambda-function/ 文章列表 本文章为系列文章 [C++11]新特性--auto的使用 http://www.so ...

  3. C++11新特性应用--实现延时求值(std::function和std::bind)

    说是延时求值,注意还是想搞一搞std::function和std::bind. 之前博客<C++11新特性之std::function>注意是std::function怎样实现回调函数. ...

  4. C++11新特性总结 (二)

    1. 范围for语句 C++11 引入了一种更为简单的for语句,这种for语句可以很方便的遍历容器或其他序列的所有元素 vector<int> vec = {1,2,3,4,5,6}; ...

  5. [转载] C++11新特性

    C++11标准发布已有一段时间了, 维基百科上有对C++11新标准的变化和C++11新特性介绍的文章. 我是一名C++程序员,非常想了解一下C++11. 英文版的维基百科看起来非常费劲,而中文版维基百 ...

  6. 在C++98基础上学习C++11新特性

    自己一直用的是C++98规范来编程,对于C++11只闻其名却没用过其特性.近期因为工作的需要,需要掌握C++11的一些特性,所以查阅了一些C++11资料.因为自己有C++98的基础,所以从C++98过 ...

  7. c++ 11 线程池---完全使用c++ 11新特性

    前言: 目前网上的c++线程池资源多是使用老版本或者使用系统接口实现,使用c++ 11新特性的不多,最近研究了一下,实现一个简单版本,可实现任意任意参数函数的调用以及获得返回值. 0 前置知识 首先介 ...

  8. C++ 11学习和掌握 ——《深入理解C++ 11:C++11新特性解析和应用》读书笔记(一)

    因为偶然的机会,在图书馆看到<深入理解C++ 11:C++11新特性解析和应用>这本书,大致扫下,受益匪浅,就果断借出来,对于其中的部分内容进行详读并亲自编程测试相关代码,也就有了整理写出 ...

  9. C++11新特性总结 (一)

    1. 概述 最近在看C++ Primer5 刚好看到一半,总结一下C++11里面确实加了很多新东西,如果没有任何了解,别说自己写了,看别人写的代码估计都会有些吃力.C++ Primer5是学习C++1 ...

随机推荐

  1. upupw phpmyadmin写shell

    废话: upupw给我的感觉是一个充当了waf毁三观的垃圾程序.然而,我讨厌的程序,管理员都特别喜欢用. 你会发现直接用之前那套写shell的建表然后在值里添加shell代码写到一个路径不可以了. C ...

  2. Linux 目录容量查询和文件打包,清空

    查看使用情况 [root@instance-0yj8cprg ~]# df -h Filesystem Size Used Avail Use% Mounted on /dev/vda1 20G 4. ...

  3. 章鱼哥出品—VB.NET DataGridView绑定数据源 &quot;与货币管理器的位置关联的行不能设置为不可见&quot; 问题的解决

    DtaGridView绑定数据源后.假设想让数据条件显示的话,直接使用  My_Row.Visible = False就会出错.错误类型是 "与货币管理器的位置关联的行不能设置为不可见&qu ...

  4. draw sin

    draw sin Steps 导入包 生成X轴,Y轴的数据点 设置输出图大小,像素,前景色 指定线宽,线型,绘制曲线 设置坐标轴范围 显示图形. Code #!/usr/bin/env python ...

  5. Linux 进程间通信(posix消息队列 简单)实例

    Linux 进程间通信(posix消息队列 简单)实例 详情见: http://www.linuxidc.com/Linux/2011-10/44828.htm 编译: gcc -o consumer ...

  6. Java 日期处理类库Joda-Time简介

    http://www.ibm.com/developerworks/cn/java/j-jodatime.html 登录 (或注册) 中文 IBM 技术主题 软件下载 社区 技术讲座 搜索 devel ...

  7. DataGridView使用技巧二:设置单元格只读

    一.修改ReadOnly属性 1.设置整个DataGridView只读: DataGridView.ReadOnly=true; 此时用户的新增行和删除行操作也被屏蔽了. 2.设置DataGridVi ...

  8. java-结合c3p0封装的db 事务 类

    将Connection对象,绑定到当前线程中去,这样在每一个方法中都能使用这个链接. DataSourceUtils.java package com.itheima.utils; import ja ...

  9. golang第三方日志包seelog配置文件详解

    开发任何项目,都离不开日志,配好自己的项目日志输出,往往是开发项目的前提.在golang中,seelog应该是比较有名的日志处理包了,功能非常强大,seelog官方文档 一.seelog主要功能下面我 ...

  10. 10 个强大的JavaScript / jQuery 模板引擎推荐

    模板引擎是为了使用户界面与业务数据(内容)分离而产生的,它可以生成特定格式的文档.由于在开发过程中,网站或应用程序的界面与数据实现分离,大大提升了开发效率,良好的设计也使得代码重用变得更加容易. 本文 ...