在C语言的时代,我们可以使用函数指针来吧一个函数作为参数传递,这样我们就可以实现回调函数的机制。到了C++11以后在标准库里引入了std::function模板类,这个模板概括了函数指针的概念
函数指针只能指向一个函数,而std::function对象可以代表任何可以调用的对象,比如说任何可以被当作函数一样调用的对象。
当你创建一个函数指针的时候,你必须定义这个函数签名(表征这个函数的入参,返回值等信息);同样的,当你创建一个std::function对象的时候,你也必须指定它所代表的可调用对象的函数签名。这一点可以通过std::function的模板参数来实现。
举个例子来说,如果要定义一个std::function对象func,这个对象可以表示任何有如下函数签名的可调用对象的,
bool(const std::unique_ptr)&, // C++11里面用来比较两个
const std::unique_ptr&) //std::unique_ptr对象的函数签名
你可以这么写,
std::function&,
const std::unique_ptr&)> func;
这是因为lambda表达式产生了可调用的对象,这个对象这里称做一个闭包(closure),可以保存在std::function对象里面。
closure(闭包)的定义是,一个函数和它所引用的非本地变量(非lambda表达式内部定义的变量)的一个集合。
参看下面一段代码,实现了一个注册回调函数的机制,
#include
void registerCallBack(std::function);
入参std::function
// 方法(A)
registerCallBack([=]{
.... // 回调函数的实现部分
})
这里使用了lambda表达式作为函数的入参,正如前面所说的lambda表达式会生成一个匿名的闭包(closure),基于这个闭包构造了一个std::function
当然我们还可以如下实现这个注册函数,入参通过const引用的方式传递,这里的引用必须是const的,这是因为调用registerCallBack函数的地方生成了一个临时的std::function()对象,是一个右值,否则编译会报错。
//方法(B)
void registerCallBack(std::function const&)
这两者的区别就在于,在registerCallBack函数内部怎么使用这个入参,如果只是简单的调用一下std::func()类,那么两种都没有问题,可能使用引用的效率更高;如果register函数内部需要保存这个std::func(),并用于以后使用,那么方法A直接保存没有问题,方法B就必须做一次拷贝,否则方法B中,当临时的对象销毁时,有可能出现引用悬空的问题。
如果我们要在registerCallBack函数内部保存这个传入的function对象,我们可以使用转移操作std::move,这样的效率更高,
class CallBackHolder
{
public:
void registerCallBack(std::function func)
{
callback = std::move(func);
}
private:
std::function callback;
}
类的成员函数都会默认有个隐藏的this指针,所以不像普通的函数直接作为入参就可以了。
std::function是通用的多态函数封装器,它的实例可以存储、复制以及调用任何可以调用的目标:函数,lambda表达式/bind表达式或其他函数对象,还有指向成员函数指针和指向数据成员指针;
std::bind接受一个函数(或者函数对象),生成一个重新组织的函数对象;
看下面一个例子,classA提供了一个注册函数,用来注册一个回调函数
class classA
{
typedef std::function callback_t;
...
void registCb(callback_t func)
{cbHandle = std::move(func);}
private:
callback_t cbHandle;
};
另一个类classB需要注册自己的一个成员函数作为回调函数到classA中,这里就可以使用std::bind函数来实现,
class classB
{
public:
classB(classA& cA)
{
cA.registCb(std::bind(&classB::handle, this, std::placeholders::_1));
}
};
Effective Modern C++中专门有一节解释过std::bind的方式比较繁琐,并且有时侯会有一些局限性,所以在引入了lambda表达式后就可以用lambda表达式来替代std::bind实现函数回调注册。
使用lambda表达式的方式可以简化这一个过程,参看如下一段代码,classB注册一个成员函数作为回调函数到classA中,classA会保存这个回调函数(std::function对象)到成员变量中,用于后面使用,
#include
#include
#include
class classA
{
typedef std::function callback_t;
public:
classA() {}
~classA() {}
void handle(int i)
{
std::cout << "classA::handle" << std::endl;
cbHandle(i);
}
void registCb(callback_t func)
{cbHandle = std::move(func);}
private:
callback_t cbHandle;
};
class classB
{
public:
classB(classA& cA)
{
cA.registCb([this](int i){classB::handle(i);});
}
~classB() {}
void handle(int i)
{
std::cout << "classB, handle message" << i << std::endl;
}
};
int main()
{
classA testa;
classB testb(testa);
testa.handle(10);
}
转自:https://www.jianshu.com/p/c4c84b073413