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

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始终能够保证与客户代码正确沟通。

 

posted on 2013-07-31 21:08  you Richer  阅读(587)  评论(0编辑  收藏  举报