C++ std function和bind的用法
C++ std function和bind的用法
c++内部存在2种函数指针,普通函数指针(和c语言一样)与成员函数指针. 两者的区别在于成员函数指针多了一个对象指针. 普通函数调用本质是通过普通函数指针实现的,成员函数本质也是通过成员函数指针实现的. 成员函数和普通函的区别在于成员函数隐藏了this指针,且该隐藏位置为成员函数参数的起始位置.
如:
class TAg
{
public:
void set(int a, unsigned int b) //注意:函数的空间和变量的空间分开,函数空间所有类共用,变量空间每个对象都有一份.
{
m_a = a;
m_b = b;
WDBG("TAg a[%],b[%]", a, b);
}
//写各种函数不影响对象空间的大小!!(兼容c语言)
//set成员函数相当于 void set(TAg *pthis, int a, unsigned int b); //第一个参数隐藏了
int m_a; //sizeof(TAg) = 8
unsigned int m_b;
};
TAg ag;
ag.set(3, 4); //实际为 TAg::set(&ag, 3, 4); //第一个隐藏为自己
结论:只要将成员函数第一个参数位置补上this,就和普通函数一致.
std::function作用: 对普通函数指针进行封装的容器,能运行函数.一般和std::bind以前匹配使用. std::bind(绑定)作用: 将其它类型函数输入转为需要的输出函数类型.可以按值或者按引用绑定数据(与仿函数类似),它包含2个方向(调用者和提供者).
调用者:为实际调用接口的语句,只有一种特定的接口方式.为提供者准备当前的参数位置别名,供提供者任意组合使用
如:typedef void (*pd)(int a, in b, in c); //最多29个参数
- 第一个参数为a 位置缩写:std::placeholders::_1
- 第二个参数为b 位置缩写:std::placeholders::_2
- 第三个参数为c 位置缩写:std::placeholders::_3
提供者:实际函数的接口拥有方,可以通过std::bind(绑定)将该接口转换为调用者的方式.
以下为实例说明用法:
class TAg
{
public:
void set(int a, unsigned int b) //类成员函数
{
m_a = a;
m_b = b;
WDBG("TAg a[%],b[%]", a, b);
}
int m_a; //sizeof(TAg) = 8
unsigned int m_b;
};
void seta(int a, unsigned int b) //普通函数
{
WDBG("set, a[%],b[%]", a, b);
}
typedef std::function<void(int,unsigned int)> TsCb; //void(int,unsigned int) 为模板参数指针的类型, void为返回参数类型,()内部为每个参数的类型.必须要匹配才能编译
//void(int,unsigned int) 为调用者希望的格式
//TAg::set 与 seta 为提供者的格式
void func()
{
TsCb cb1(&seta); //普通函数调用(一致使用,不需要更改)
cb1(2, 3);
//输出为 set, a[2],b[3]
}
void func2()
{
TAg ab;
TAg *pab = &ab; //bind的对象必须为指针类型,不要用&ab
TsCb cb2(std::bind(&TAg::set, pab, std::placeholders::_1, std::placeholders::_2)); //将成员函数变为普通函数
//注意std::bind可以绑定任意多个参数,也可以按引用或指针捕获(注意生命周期).
//如果按值时仅为当时值的副本
//std::placeholders::_1 为:set函数的参数a位置对应 函数调用的第一个位置
//std::placeholders::_2 为:set函数的参数b位置对应 函数调用的第二个位置
//也可以自己反过来安排..
cb2(2, 3); //调用函数
//输出为 TAg a[2],b[3]
if(cb2) //如果需要判断是否为空 通过 重载 explicit operator bool()
{
cb2(2, 3);
}
{ //反过顺序例子
TsCb cb3(std::bind(&TAg::set, pab, std::placeholders::_2, std::placeholders::_1)); //将成员函数变为普通函数
cb3(2, 3); //调用函数 (输入方式没变,但输出变反)
//输出为 TAg a[3],b[2]
}
{ //或者都一样, 可以自由安排
TsCb cb4(std::bind(&TAg::set, pab, std::placeholders::_1, std::placeholders::_1)); //将成员函数变为普通函数
cb4(2, 3); //调用函数 (输入方式没变,但输出变反)
//输出为 TAg a[2],b[2]
}
{ //超过提供范围会报错
TsCb cb5(std::bind(&TAg::set, pab, std::placeholders::_1, std::placeholders::_3)); //编译不过!!!
}
}