系列文章目录
C++技能系列
Linux通信架构系列
C++高性能优化编程系列
深入理解软件架构设计系列
高级C++并发线程编程
期待你的关注哦!!!
现在的一切都是为将来的梦想编织翅膀,让梦想在现实中展翅高飞。
Now everything is for the future of dream weaving wings, let the dream fly in reality.
可调用对象、std::function与std::bind
- 系列文章目录
- 一、可调用对象
- 1、函数指针
- 2、具有operator( )成员函数的类对象(仿函数或函数对象)
- 3、可被转换为函数指针的类对象
- 4、类成员函数指针
- 二、std::function可调用对象包装器
- 1、绑定普通函数
- 2、绑定类的静态成员函数
- 3、绑定仿函数
- 4、范例演示
- 三、std::bind 绑定器
- 四、小结
一、可调用对象
1、函数指针
调用函数,就是一个可调用对象。
看如下范例:
//myfunc函数的定义
void myfunc(int tv)
{
cout << "myfunc()函数执行了, tv = " << tv << endl;
}
//在main主函数中,加入如下代码
void (*pmf)(int) = myfunc; // 定义函数指针并给初值,myfunc也可以写成&myfunc,是一样的。
pmf(15);//调用函数,就是一个可调用对象
2、具有operator( )成员函数的类对象(仿函数或函数对象)
仿函数的定义:仿函数(functors)又称为函数对象(function objects),是一个能行驶函数功能的类所定义的对象。仿函数的语法几乎和普通函数调用一样。
看如下范例:
class TC
{
public:
void operator()(int tv)
{
cout << "TC::operator()执行了,tv= " << tv << endl;
}
};
//在mian主函数中,加入如下代码
TC tc;
tc(20);//调用的是( )操作符,这就是一个可调用对象,等价于tc.operator()(20);
3、可被转换为函数指针的类对象
可被转换为函数指针的类对象也可以叫作仿函数或函数对象。
看如下范例:
class TC2
{
public:
using tfpoint = void(*)(int);
static void myfunc(int tv) //静态成员函数
{
cout << "TC2::mysfunc()静态成员函数执行了,tv=" << tv << endl;
}
operator tfpoint() //类型转换运算符/类型转换函数
{
return mysfunc;
}
}
//在mian主函数中,加入如下代码
TC2 tc2
tc2(50);//先调用tfpoint,再调用mysfunc;这就是一个可调用对象,等价于tc2.operator TC2::tfpoint()(50);
4、类成员函数指针
看如下范例:
class TC
{
public:
void operator()(int tv)
{
cout << "TC::operator()执行了,tv= " << tv << endl;
}
void ptfunc(int tv){
cout << "TC::ptfunc()执行了,tv = " << tv << endl;
}
private:
int m_a;
};
//在mian主函数中,加入如下代码
TC tc3;
//类成员函数指针变量myfpoint定义并给初值
void (TC::* myfpointpt)(int) = &TC::ptfunc;
//要调用成员函数,就必须用到对象tc3
(tc3.*myfpointpt)(68);
那么,有没有什么方法能够把这些可调用对象的调用形式统一一下呢?有,那就是使用std::function把这些可调用对象包装起来。
二、std::function可调用对象包装器
std::function
的头文件是#include< functional >
,这个类模版用来包装各种可调用对象,比较遗憾的是它不能装类成员函数指针,因为类成员函数指针是需要类对象参与才能完成的。
std::function
类模版的特点是:通过指定模版参数,它能够用统一的方式来处理各种可调用对象。
1、绑定普通函数
看如下范例:
//绑定一个普通函数,注意< >中的格式
std::function<void(int)> f1 = myfunc;
//调用普通函数
f1(100);
2、绑定类的静态成员函数
看如下范例:(在TC类增加一个public修饰的静态成员函数)
class TC
{
public:
void operator()(int tv)
{
cout << "TC::operator()执行了,tv= " << tv << endl;
}
static int stcfunc(int tv)
{
cout << "TC::stcfunc()静态成员函数执行了,tv = " << tv << endl;
return tv;
}
};
//在mian主函数中,加入如下代码
//绑定一个类的静态成员函数
std::function<int(int)> fs2 = TC::stcfunc;
//调用静态成员函数
fs(110);
3、绑定仿函数
class TC
{
public:
TC() //构造函数
{
m_a = 1;
}
void operator()(int tv)
{
cout << "TC::operator()执行了,tv= " << tv << endl;
}
private:
int m_a;
};
//在mian主函数中,加入如下代码
TC tc3;
std::function<void(int)> f3 = tc3;
f3(120); //TC::operator()执行了,tv = 120
4、范例演示
范例一:
class CB
{
std::function<void()>fcllback;
public:
CB(const std::function<void()> &f):fcallback(f)
{
int i;
i = 1;
}
void runcallback(void)
{
fcallback();
}
};
class CT
{
public:
CT()
{
cout << "CT::CT()执行" << endl;
}
CT(const CT&) //拷贝构造函数
{
cout << "CT::CT(const CT&)执行" << endl;
}
void operator()(void)
{
cout << "CT::operator()执行" << endl;
}
};
int main()
{
CT ct;
CB cb(ct);
cb.runcallback();
return 0;
}
范例二:
void mycbk(int cs, const std::function<void(int)>& f)
{
f(cs);
}
void runfunc(int x)
{
cout << x <<endl;
}
int main()
{
for(int i = 0; i < 10; i ++)
{
mycbk(i, runfunc);
}
return 0;
}
三、std::bind 绑定器
std::bind
是一个函数模版,这个函数模版的头文件#include< functional >
。std::bind
能将对象以及相关的参数绑定到一起,绑定完后可以直接调用,也可以用std::function进行保存,在需要的时候调用。该函数的一般格式如下:
std::bind(带绑定的函数对象/函数指针/成员函数指针, 参数绑定值1, 参数绑定值2, ..., 参数绑定值n);
std::bind
有两个意思:
- 将可调用对象和参数绑定在一起,构成一个仿函数,所以可以直接调用。
- 如果函数有多个参数,可以绑定部分参数,其他的参数在调用的时候指定。
下面我们通过范例来理解这个函数模版的使用:
详解看备注
void myfunc1(int x, int y, int z)
{
cout << "x=" << x << ",y=" << y << ",z=" << z << endl;
}
void myfunc2(int &x, int &y)
{
x++;
y++;
}
class CQ
{
public:
void myfunpt(int x, int y)
{
cout << "x=" << x << ",y=" << y << endl;
m_a = x;
}
private:
int m_a = 0;
}
int main()
//在main主函数
{
//😄💪🏻使用方式一
{
//其实返回值是一个仿函数对象,可以直接调用,也可以赋给std::function
auto bf1 = std::bind(myfunc1, 10, 20, 30);
//执行myfunc1函数,结果:x=10,y=20,z=30
bf1();
}
//😄💪🏻使用方式二
{
//也可以使用placeholders::占位符
auto bf2 = std::bind(myfunc1, placeholders::_1, placeholders::_2, 30);
bf2(5, 15);//结果:x=5,y=15,z=30
}
//😄💪🏻使用方式三
{
//也可以直接调用
std::bind(myfunc1, placeholders::_1, placeholders::_2, 30)(10, 20);//x=10,y=20,z=30
}
//😄💪🏻使用方式四
{
//调换参数
auto bf3 = std::bind(myfunc1, placeholders::_2, placeholders::_1, 30);
bf2(5, 15); //结果:x=15,y=5,z=30
}
//😄💪🏻使用方式五
{
int a = 2;
int b = 3;
auto bf4 = std::bind(myfunc2, a, placeholders::_1);
bf4(b); //执行后结果:a=2,b=4。
//这说明:bind对于预先绑定的函数的参数是通过值传递的所以这个a是值传递的。
//bind对于不事先绑定的参数,通过std::placeholders传递的参数是
//通过引用传递的,所以这个b实际上是引用传递的
}
//😄💪🏻使用方式六
{
//一个类对象
CQ cq;
//类函数有绝对地址,和对象无关,但要被调用必须有类对象参数
auto bf5 = std::bind(&CQ::myfunpt, cq, placeholders::_1, placeholders::_2);
//对成员函数的调用
bf5(10, 20); //结果为:x=10,y=20
//⚠️上面的的第二个参数cq会导致生成一个临时的CQ对象,修改其m_a的值是不会改变真实cq对象,
//⚠️cq前面加&,这样就不会导致生成一个临时的CQ对象,可以改变m_a的值
//⚠️auto bf5 = std::bind(&CQ::myfunpt, &cq, placeholders::_1, placeholders::_2);
}
//😄💪🏻使用方式七
{
//bind和function配合使用(bind返回值直接赋给std::function类型)
std::function<void(int, int)> bfc6 = std::bind(&CQ::myfunpt, cq, placeholders::_1, placeholders::_2);
bfc6(10, 20);
}
//😄💪🏻使用方式八
{
//绑定成员变量
std::function<int&(void)>bf7 = std::bind(%CQ::m_a, &cq);
bf7() = 60; //执行后cq对象的m_a的成员变量值变为了60了
}
return 0;
}
四、小结
因为有了占位符(placeholder
)这种概念,所以std::bind
使用变得更加灵活。
std::bind 也可以绑定部分参数,绑定部分参数时,就需要通过std::placeholder
来决定bind
所在的位置的参数将会属于调用发生时的第几个参数。
std::bind
的思想实际上就是一种延迟计算的思想,将可调用对象保存起来,然后在需要的时候调用。std::function
一般要绑定一个可调用对象,类成员函数不能被绑定。而std::bind更加强大,成员函数、成员变量等都能绑定。现在通过std::function
和std::bind
配合使用,所有的可调用对象都有了统一的操作方法。