function是一个函数对象的“容器”,概念上像是c/c++中函数指针类型的泛化,是一种“智能函数指针”。它以对象的形式封装了原始的函数指针或函数对象,能够容纳任意符合函数签名的可调用对象。

因此,它可以被用于回调机制,暂时保管函数或函数对象,在之后需要的时机再调用,使回调机制拥有更多的弹性。

1、function的声明

比如使用function<int(doublea, double b)> func;

这就是声明了一个function对象,返回值为int,有两个参数都为double

2、操作函数

无参的构造函数或者传入空指针构造将创建一个空的function对象,不持有任何可调物,调用空的function对象将抛出bad_function_call异常,因此在使用function前最后检测一下它的有效性。

可以用empty()测试function是否为空,或者用重载操作符operaor!来测试。function对象也可以在一个bool上下文中直接测试它是否为空,他是类型安全的。

3、比较操作

function重载了比较操作符operator==和operator!=,可以与被包装的函数或函数对象进行比较。如果function存储的是函数指针,那么比较相当于:

function.target<Function>() == func_pointer

例如

function<int(int a, int b)> func(f);
assert(func == f);

如果function存储的是函数对象,那么要求函数对象必须重载了operator==,是可比较的。

两个function对象不能使用==或!=直接比较,这是特意的。因为function存在到bool的隐式转换,function定义了两个function对象的operator==但没有实现,企图比较两个function对象会导致编译错误。

4、用法

#include "stdafx.h"
#include "boost/utility/result_of.hpp"
#include "boost/typeof/typeof.hpp"
#include "boost/assign.hpp"
#include "boost/ref.hpp"
#include "boost/bind.hpp"
#include "boost/function.hpp"
#include "iostream"
using namespace std; int f(int a, int b)
{
return (a + b);
} int _tmain(int argc, _TCHAR* argv[])
{
boost::function<int(int a, int b)> func;
func = f; if (func)
{
cout << func(10, 20) << endl;
} func = 0; assert(!func.empty()); return 0;
}

function这种能够容纳任意可调用对象的能力是非常重要的,在编写泛型代码的时候尤其有用,它使我们能够接受任意的函数或函数对象,增加程序的灵活性。

与原始的函数指针相比,function对象的体积要稍微大一点(3个指针的大小),速度要稍微慢一点(10%左右的性能差距),但这与它带给程序的巨大好处相比是无足轻重的。

只要函数签名式一致,function也可以存储成员函数和函数对象,或者bind表达式的结果:

#include "stdafx.h"
#include "boost/utility/result_of.hpp"
#include "boost/typeof/typeof.hpp"
#include "boost/assign.hpp"
#include "boost/ref.hpp"
#include "boost/bind.hpp"
#include "boost/function.hpp"
#include "iostream"
using namespace std; struct DemoClass
{
int add(int a, int b)
{
return (a + b);
} // int operator()(int a, int b) const
// {
// return (a + b);
// }
}; int _tmain(int argc, _TCHAR* argv[])
{
DemoClass democlass; boost::function<int(DemoClass&, int, int)> func;
// func = boost::bind(&DemoClass::add, _1, _2, _3);
func = boost::bind(&DemoClass::add, democlass, _2, _3); cout << func(democlass, 10, 20) << endl; return 0;
}

5、使用ref库

function使用拷贝语义保存参数,当参数很大时拷贝的代价往往很高, 或者有时候不能拷贝参数,这时就使用ref库。

function并不要求ref库提供operator(),因为它能够自动识别包装类reference_wraooer<T>,并调用get()方法获得被包装的对象:

#include "stdafx.h"
#include "boost/utility/result_of.hpp"
#include "boost/typeof/typeof.hpp"
#include "boost/assign.hpp"
#include "boost/ref.hpp"
#include "boost/bind.hpp"
#include "boost/function.hpp"
#include "iostream"
using namespace std; struct DemoClass
{
// int add(int a, int b)
// {
// return (a + b);
// } int operator()(int a, int b) const
{
return (a + b);
}
}; int _tmain(int argc, _TCHAR* argv[])
{
DemoClass democlass;
boost::function<int(int x, int y)> func;
func = cref(democlass); // 使用cref()函数包装常对象的引用
cout << func(10, 20) << endl; // 调用被引用的对象 return 0;
}

6、用于回调

function可以容纳任意符合函数签名式的可调用物,因此它非常适合代替函数指针,存储用于回调的函数,而且它的强大功能会使代码更灵活、富有弹性。

看看下面的例子:

#include "stdafx.h"
#include "boost/utility/result_of.hpp"
#include "boost/typeof/typeof.hpp"
#include "boost/assign.hpp"
#include "boost/ref.hpp"
#include "boost/bind.hpp"
#include "boost/function.hpp"
#include "iostream"
using namespace std; class DemoClass
{
public:
// int add(int a, int b)
// {
// return (a + b);
// }
DemoClass(int i) : n(i)
{ } int operator()(int a, int b) const
{
return (a + b);
} template<typename CallBack>
void accept(CallBack f)
{
func_ = f;
} void run()
{
func_(n);
}
private:
typedef boost::function<void(int)> func_t;
func_t func_;
int n;
}; void call_back_func(int i)
{
cout << "call back func : ";
cout << i * 2 << endl;
} int _tmain(int argc, _TCHAR* argv[])
{
DemoClass dc(19);
dc.accept(call_back_func);
dc.run(); return 0;
}

这就是一个简单使用function作为函数回调的例子。

DemoClass使用模板函数accept()接受回调函数。之所以使用模板函数是因为这种形式更加灵活,用户可以在不知道也不关心内部存储形式的情况下传递任何可调用对象,包括函数指针和函数对象。

使用普通的C函数进行回调并不能体现function的好处,下面来看一个带状态的函数对象,并使用ref库传递引用:

#include "stdafx.h"
#include "boost/utility/result_of.hpp"
#include "boost/typeof/typeof.hpp"
#include "boost/assign.hpp"
#include "boost/ref.hpp"
#include "boost/bind.hpp"
#include "boost/function.hpp"
#include "iostream"
using namespace std; class DemoClass
{
public:
DemoClass(int i) : n(i)
{ } template<typename CallBack>
void accept(CallBack f)
{
func_ = f;
} void run()
{
func_(n);
}
private:
typedef boost::function<void(int)> func_t;
func_t func_;
int n;
}; class call_back_obj
{
public:
call_back_obj(int x) : x_(x)
{ } void operator()(int i)
{
cout << "call_back_obj : " << i * x_++ << endl;
} private:
int x_; // 内部状态
}; int _tmain(int argc, _TCHAR* argv[])
{
DemoClass dc(10);
call_back_obj cbo(2); dc.accept(boost::ref(cbo));
dc.run();
dc.run(); return 0;
}

DemoClass因为使用了function作为内部可调用物的存储,因此不用做任何改变,即可接受函数指针也可以接受函数对象,给用户以最大的方便。

function还可以搭配bind库,把bind表达式作为回调函数,可以接受类成员函数,或者把不符合函数签名式的函数bind变为可接受的形式,下面是一个例子:

#include "stdafx.h"
#include "boost/utility/result_of.hpp"
#include "boost/typeof/typeof.hpp"
#include "boost/assign.hpp"
#include "boost/ref.hpp"
#include "boost/bind.hpp"
#include "boost/function.hpp"
#include "iostream"
using namespace std; class DemoClass
{
public:
DemoClass(int i) : n(i)
{ } template<typename CallBack>
void accept(CallBack f)
{
func_ = f;
} void run()
{
func_(n);
}
private:
typedef boost::function<void(int)> func_t;
func_t func_;
int n;
}; class call_back_factory
{
public:
void call_back_func1(int i)
{
cout << "call_back_func1 : " << i * 2 << endl;
}
void call_back_func2(int i, int j)
{
cout << "call_back_func2 : " << i * j * 2 << endl;
}
}; int _tmain(int argc, _TCHAR* argv[])
{
DemoClass dc(10);
call_back_factory cbf;
dc.accept(boost::bind(&call_back_factory::call_back_func1, boost::ref(cbf), _1));
dc.run(); dc.accept(boost::bind(&call_back_factory::call_back_func2, boost::ref(cbf), _1, 2));
dc.run(); return 0;
}

通过以上的示例代码,可以看到function用于回调的好处,它无需改变回调的接口就可以解耦客户代码,是客户代码不必绑死在一种回调形式上,进而可以持续演化。而function始终能够保证与客户代码正确沟通。

boost------function的使用(Boost程序库完全开发指南)读书笔记的更多相关文章

  1. boost------signals2的使用2(Boost程序库完全开发指南)读书笔记

    1.应用于观察者模式 本小节将使用signals2开发一个完整的观察者模式示例程序,用来演示信号/插槽的用法.这个程序将模拟一个日常生活场景:客人按门铃,门铃响,护士开门,婴儿哭闹. Ring.h: ...

  2. boost------asio库的使用2(Boost程序库完全开发指南)读书笔记

    网络通信 asio库支持TCP.UDP.ICMP通信协议,它在名字空间boost::asio::ip里提供了大量的网络通信方面的函数和类,很好地封装了原始的Berkeley Socket Api,展现 ...

  3. boost------signals2的使用1(Boost程序库完全开发指南)读书笔记

    signals2基于Boost的另一个库signals,实现了线程安全的观察者模式.在signals2库中,观察者模式被称为信号/插槽(signals and slots),他是一种函数回调机制,一个 ...

  4. boost------asio库的使用1(Boost程序库完全开发指南)读书笔记

    asio库基于操作系统提供的异步机制,采用前摄器设计模式(Proactor)实现了可移植的异步(或者同步)IO操作,而且并不要求多线程和锁定,有效地避免了多线程编程带来的诸多有害副作用. 目前asio ...

  5. boost------bind的使用(Boost程序库完全开发指南)读书笔记

    bind是c++98标准库中函数适配器bind1st/bind2nd的泛化和增强,可以适配任意的可调用类型,包括函数指针.函数引用.成员函数指针和函数对象. 1.工作原理 bind并不是一个单独的类或 ...

  6. [转] boost------ref的使用(Boost程序库完全开发指南)读书笔记

    http://blog.csdn.net/zengraoli/article/details/9663057 STL和Boost中的算法和函数大量使用了函数对象作为判断式或谓词参数,而这些参数都是传值 ...

  7. boost------ref的使用(Boost程序库完全开发指南)读书笔记

    STL和Boost中的算法和函数大量使用了函数对象作为判断式或谓词参数,而这些参数都是传值语义,算法或函数在内部保修函数对象的拷贝并使用,例如: #include "stdafx.h&quo ...

  8. node.js开发指南读书笔记(1)

    3.1 开始使用Node.js编程 3.1.1 Hello World 将以下源代码保存到helloworld.js文件中 console.log('Hello World!'); console.l ...

  9. Ngine X 完全开发指南 读书笔记-前言

    一开始接触的编程语言是VF,那是一种可视化编程语言,所谓的可视化,就是运行结果能直接看得到的,非常直观,便于调试,适合刚刚接触编程的新人学习.当时学得懵懂,半知半解,就是感觉程序非常神奇,常常几句代码 ...

随机推荐

  1. <四> jQuery 事件

    $(document).ready(function) 将函数绑定到文档的就绪事件(当文档完成加载时) $(selector).click(function) 触发或将函数绑定到被选元素的点击事件 $ ...

  2. mysql的错误:The server quit without updating PID file /usr/local/mysql/data/door.pid).

    mysql错误解决: 先 参考:http://www.jb51.net/article/48625.htm 参考第四条: mysql在启动时没有指定配置文件时会使用/etc/my.cnf配置文件,请打 ...

  3. java连接mysql数据库(jsp显示和控制台显示)

           很多事情,在我们没有做之前我们觉得好难,但是只要你静下心来,毕竟这些都是人搞出来的,只要你是人,那就一定可以明白. 配置:JDK1.8,MySQL5.7,eclipse:Neon Rel ...

  4. java.util.ArrayList

    /* * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved. * ORACLE PROPRIETA ...

  5. C语言预处理运算符

    转自C语言预处理运算符 预处理还需要运算符?有没有搞错? ^_^, 没有搞错,预处理是有运算符,而且还不止一个: #(单井号)    -- 字符串化运算符. ##(双井号 )-- 连接运算符 #@   ...

  6. Android-java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()

    章出自:luchg技术交流 http://www.luchg.com 版权所有.本站文章除注明出处外,皆为作者原创文章,可自由引用,但请注明来源,谢谢. Android-java.lang.Runti ...

  7. DHTMLX 前端框架 建立你的一个应用程序 教程(六)-- 表格加载数据

    从数据库加载数据 这篇我们介绍从MySQL数据库中加载数据到表格 我们使用 MySql的数据库dhtmlx_tutorial 和表contacts 示例使用的是PHP平台和dhtmlxConnecto ...

  8. UL LI 布局 TAB 切换条

    web页面实现tab的功能有几种实现方式,下面是使用UL LI DIV方式实现的tab. <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Tr ...

  9. Java:List,ArrayList和LinkList的区别

    1.大学数据结构中ArrayList是实现了基于动态数组的数据结构,LinkList基于链表的数据结构 2.对于随机访问get和set,ArrayList优于LinkList,因为LinkedList ...

  10. ☀【DOM对象 / jQuery对象】

    jQuery对象和DOM对象 √http://www.ituring.com.cn/article/38868 <!DOCTYPE html> <html lang="zh ...