系列文章目录
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::functionauto bf1 = std::bind(myfunc1, 10, 20, 30);//执行myfunc1函数,结果:x=10,y=20,z=30bf1();}//😄💪🏻使用方式二{//也可以使用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
配合使用,所有的可调用对象都有了统一的操作方法。