1、简介
function是一个模板类,它就像一个包装了函数指针的容器(只有一个元素),function对象可以存储函数指针、Lambda表达式、函数对象(比如bind表达式的结果)。function非常适合代替函数指针,存储用于回调的函数,我们完全可以使用function其来代替函数指针。
如下定义了一个能够容纳void(int)类型的function对象:
function<void(int)> funo;
使用function需要包含头文件"boost/function.hpp",C++11已经支持function。
一些成员函数:
target():返回对象内部的可调用物Functor的指针,对象为空则返回NULL
contains():检测是否持有一个Functor。
clear():将对象清空,与"= 0"效果相同。
empty():测试对象是否为空,也可以在一个bool上下文中直接测试它是否为空。
operator==、operator!=:与一个函数或函数对象进行比较。
operator():调用内部的可调用物,它也会将参数传给可调用物。
2、简单使用
int FuncName(int a, int b)
{
return a + b;
}
int main()
{
function<int(int, int)> funo;
funo = FuncName;
if (funo)
{
cout << funo(3, 4) << endl;
}
return 0;
}
3、用于回调
①、存储普通函数
以下为在CTestClass类中使用function对象m_fFun来保存回调函数和调用回调函数的示例,CTestClass的成员函数SetCallBack()被定义成了模板函数,这样更方便:
void call_back_func(int i)
{
cout << i << endl;
}
class CTestClass
{
public:
CTestClass(int i) :m_iNum(i) {}
public:
template<typename CallBack>
void SetCallBack(CallBack f)
{
m_fFun = f;
}
void run()
{
if(m_fFun)
m_fFun(m_iNum);
}
private:
function<void(int)> m_fFun;
int m_iNum;
};
int main()
{
CTestClass dc(10);
dc.SetCallBack(call_back_func);
dc.run();
return 0;
}
②、存储Lambda表达式
class Bar { public: std::function<void()> onClick; }; class Foo { public: Foo() { b = new Bar; //存储Lambda,捕获this指针,使lambda内可调用本类的成员 b->onClick = [this] { func(); }; } void func() { cout << "func" << endl; } private: Bar* b; }; int main() { //存储Lambda,捕获变量num int num = 100; std::function<void()> f = [num] { cout << "num:" << num << endl; }; f(); getchar(); return 0; }
③、存储函数对象
使用function来存储函数对象是function的重要功能之一,因为相比于使用普通函数回调,使用函数对象回调可以保存数据和实现复杂的操作。以下为在CTestClass类中使用function对象m_fFun来保存回调函数对象和调用回调函数对象的示例
#include "boost/bind.hpp" class CFunObj { public: CFunObj(int i) :x(i) {} public: void operator()(int i) { cout << i * x++ << endl; } private: int x; }; class CTestClass { public: CTestClass(int i) :m_iNum(i) {} public: template<typename T> void SetCallBack(T f) { m_fFun = f; } void run() { if (m_fFun) m_fFun(m_iNum); } private: function<void(int)> m_fFun; int m_iNum; }; int main() { CTestClass dc(10); CFunObj cfo(2); dc.SetCallBack(ref(cfo)); dc.run(); dc.run(); return 0; }
function使用拷贝语义来保存函数或函数对象,当函数或函数对象很复杂或者禁止拷贝的时候可以使用ref()以解决拷贝的问题。从以上示例代码可以看到,在使用SetCallBack设置回调的时候就是使用ref()来传递的引用包装器,SetCallBack是模板函数,所以T类型在函数调用的时候就是ref()引用包装类型。
④、存储类的成员函数
以下我们定义了一个函数工厂类,回调函数都在这个工厂类中定义,我们可以配合bind来保存类中的回调函数,然后通过fiunction对象再调用类中的回调函数:
#include "boost/bind.hpp" class CCall_back_factory { public: void call_back_fun_1(int i) { cout << i * 2 << endl; } void call_bcak_fun_2(int i, int j) { cout << i * j * 2 << endl; } }; class CTestClass { public: CTestClass(int i) :m_iNum(i) {} public: template<typename CallBack> void SetCallBack(CallBack f) { m_fFun = f; } void run() { if(m_fFun) m_fFun(m_iNum); } private: function<void(int)> m_fFun; int m_iNum; }; int main() { CTestClass dc(10); CCall_back_factory cbf; dc.SetCallBack(bind(&CCall_back_factory::call_back_fun_1, cbf, _1)); dc.run(); int j = 5; dc.SetCallBack(bind(&CCall_back_factory::call_bcak_fun_2, cbf, _1, j)); dc.run(); return 0; }
通过以上示例代码可以看到,function用于回调再配合bind解决了类的成员函数不能作为回调函数的问题,而且使用更加灵活。 C++中A类对象里调用B类对象的成员函数一般使用以下三种方法:
①、使用虚函数:在A类中保存B类对象的基类指针m_ptr,在B类中重写基类的虚函数,通过B类的基类指针m_ptr来调用B类的虚函数。
②、使用function + bind:在A类中保存B类对象的成员函数的function(通过bind),通过function来调用B类的成员函数。
③、使用function + lambda:在A类中保存B类对象的成员函数的function(通过捕获this指针的lambda),通过function来调用B类的成员函数。
4、给线程函数传入多个参数
我们知道,线程函数只有一个void*参数,利用bind和function可以间接实现给线程函数传入多个参数:
#include <Windows.h>
#include "boost/bind.hpp"
void RealFun(char* p, int& n)
{
cout << p << n << endl;
n = 0;
}
DWORD WINAPI ThreadFunc(void* param)
{
function<void()> func = *((function<void()>*)param);
func();
return 0;
}
int main()
{
char* p = "value is :";
int i = 10;
function<void()> func = boost::bind(&RealFun, p, ref(i));
HANDLE hThread = CreateThread(NULL, 0, ThreadFunc, &func, 0, 0);
WaitForSingleObject(hThread, INFINITE);
assert(i == 0);
return 0;
}