这一篇讲讲bind,话说bind-function技术有一段时间很潮啊,扬言分分钟灭掉面向对象编程妥妥的,让*的设计模式再也无用武之地,其实听到这个消息我非常的郁闷,因为设计模式我还没有怎么学会,就眼看要被淘汰了啊。所以我们现在看看bind-function的用法吧,我用的是C++11中,std的bind而不是boost的bind。
一,可调用对象
C++中有一个概念叫做可调用对象,让截然不同的概念,加个括号,填写参数就能够统一使用,真是让人震惊了。比如在加个括号,填写参数后,
1,函数指针,相当调用了这个函数;
2,函数对象,相当调用该对象的.operator(params)方法;
3,成员函数指针,这个在前面加了调用它的对象后,就变为这个对象调用该方法。
二,利用std::funciton模板对上面三个内容进行接收
.h
class BindDemo
{
public:
BindDemo();
void useFunction();
static double aMemberFunc(double param);
int operator()(int param);
};
inline double aFreeFunc(double param);
.cpp
#include "binddemo.h"
#include <functional>
#include <iostream>
BindDemo::BindDemo()
{
useFunction();
}
void BindDemo::useFunction()
{
//1
std::function<double(double)> fr1 = aFreeFunc;
std::cout<<fr1(5)<<std::endl;
//2
std::function<double(double)> fr2 = BindDemo::aMemberFunc;
std::cout<<fr2(6)<<std::endl;
//3
std::function<double(double)> fr3 = *this;
std::cout<<fr3(7)<<std::endl;
}
double BindDemo::aMemberFunc(double param)
{
return param * 3;
}
int BindDemo::operator()(int param)
{
return param *5;
}
double aFreeFunc(double param)
{
return param * 2;
}
在useFunction函数中定义了三个function对象,分别托管了*函数、类静态函数、函数对象。遗憾的是不能托管类成员函数。
四,利用std::bind函数创建闭包
这里再提一个闭包的概念,我觉得通俗的来说,就是闭包将所有需要的内容都放进去了,或者是只留出个别需要调用者填写的内容,当调用时能够统一进行调用。这样说起来一点都不通俗啊。
.h
void useBind();
.cpp
void BindDemo::useBind()
{
//1
using namespace std::placeholders;
std::cout<<std::bind(aFreeFunc,4)()<<std::endl;
std::cout<<std::bind(aFreeFunc,_1)(4)<<std::endl;
//2
std::cout<<std::bind(&BindDemo::aMemberFunc2,*this,4,5,6)()<<std::endl;
std::cout<<std::bind(&BindDemo::aMemberFunc2,this,4,5,6)()<<std::endl;
//3
std::cout<<std::bind(&BindDemo::aMemberFunc,6)()<<std::endl;
// std::cout<<std::bind(&BindDemo::aMemberFunc,this,6)()<<std::endl;
//4
std::cout<<std::bind(*this,6)()<<std::endl;
}
其实也很容易看懂(如果看了第一篇博客的话),第一个参数就是函数指针,注意了*函数指针和成员函数指针的区别就行了。其中:
1是绑定*函数,不利用占位符或者是利用占位符的。
2是绑定成员函数,传入的实例对象可以为实例的指针,也可以实例。这里特别要注意一下,如果传入的是实例,那么是重新构造了一个实例对象才穿进去的,如果需要穿个引用进去可以这样:
std::cout<<std::bind(&BindDemo::aMemberFunc2,std::ref(*this),4,5,6)()<<std::endl;
也就是利用std::ref()包装一下,传入引用进去。(其实我写到这里才明白std::ref有什么用,以前还觉得真没用,这才知道,参数的类型是由函数自己定义的,而函数如果本身参数是值类型,那么用户向穿引用进去怎么办?好办,就用std::ref()包装一下即可)。
3是绑定静态函数,注释部分错误
4是绑定函数对象。
另外再注意一下占位符的写法。
五,bind和function的组合
.h
void useBindWithFunction();
.cpp
void BindDemo::useBindWithFunction()
{
//1
using namespace std::placeholders;
std::function<double()> fc1 = std::bind(aFreeFunc,4);
std::cout<<fc1()<<std::endl;
std::function<double(double)> fc2 = std::bind(aFreeFunc,_1);
std::cout<<fc2(2.0)<<std::endl;
//2
std::function<double()> fc3 = std::bind(&BindDemo::aMemberFunc2,*this,4,5,6);
std::cout<<fc3()<<std::endl;
std::function<double(double)> fc4 = std::bind(&BindDemo::aMemberFunc2,*this,_1,5,6);
std::cout<<fc4(2.2)<<std::endl;
std::function<double(double,double)> fc5 = std::bind(&BindDemo::aMemberFunc2,*this,_1,_2,6);
std::cout<<fc5(2.0,3.1)<<std::endl;
//3
std::function<int()> fc6 = std::bind(*this,6);
std::cout<<fc6()<<std::endl;
}
这个也写的比较清楚,前面说过,function接收*函数指针、成员变量指针、函数对象,而bind的作用就是把各种不同的*函数指针、成员变量指针、函数对象打包成为一个样式的function,这样就能统一调用——这就是传说中的利用function-bind将设计模式赶出历史舞台的基础。
1是*函数,2是成员函数,3是函数对象。
需要注意的是,function尖括号里面的容,要和bind形成的内容一致,也就是如在1中,std::function<double()> fc1 = std::bind(aFreeFunc,4);的bind不需要再传入参数,所以尖括号里面类型的参数列表为void,而std::function<double(double)> fc2 = std::bind(aFreeFunc,_1);有一个占位符,那么尖括号里的参数列表为double。
还需要注意的是,bind返回的是一个指针,function这边接收时,不能用引用接收。
这是一个很好的用法,下面给出例子。
六,bind在回调函数中变参数的应用
要求,回调函数的参数个数大于等于function定义的个数。
.h
double aMemberFunc3(int param1,double param2);
void callBackFunc(std::function<double(int,double)> func);
void useCallBackFunc();
类外定义:
inline double aFreeFunc2(int param1,double param2);
.cpp
double BindDemo::aMemberFunc3(int param1, double param2)
{
return param1 +param2;
}
void BindDemo::callBackFunc(std::function<double (int, double)> func)
{
std::cout<<func(5,3.2)<<std::endl;
}
void BindDemo::useCallBackFunc()
{
using namespace std::placeholders;
callBackFunc(aFreeFunc2);
callBackFunc(std::bind(&BindDemo::aMemberFunc3,this,_1,_2));
callBackFunc(std::bind(&BindDemo::aMemberFunc2,this,2.3,_1,_2));
}
double aFreeFunc2(int param1, double param2)
{
return param1 + param2;
}
可以看到,useCallBackFunc中对*函数回调很简单,就穿进去即可,只要参数对的上,就能运行了。而成员函数的回调就比较麻烦,主要是还要传一个实例对象(或指针)进去,所以必须用bind。而第四句话就是bind我觉得最为重要的用法,将当前所在域的变量传进去,将多于需求(但是也是要满足需求参数的类型的)的内容传个内容进去即可。这其实和lambda用法是一样的,只是lambda在写内容时就知道是否用了环境变量,而这个是在调用时知道是否用了环境变量。