一 可调用对象
本节课将可调用对象整理一下
1 函数指针
void func90(int tv) {cout << "func90(int tv) called tv = " << tv << endl;
}void main() {//可调用对象 1 函数指针//方式一 1.1定义一个函数指针对象pmfvoid(*pmf)(int);//1.2将函数名赋值给函数指针pmf = func90;//1.3通过函数指针调用该函数pmf(2);//方式二 1.1 定义一个函数指针对象pmf1,并直接将函数名赋值给pmf1函数指针void(*pmf1)(int) = func90;//1.2通过函数指针调用该函数pmf1(20);
}
2.具有operator()成员函数的类对象(仿函数)
仿函数 functor,通过在类中重载()运算符 实现
//2.具有operator()成员函数的类对象仿函数()
class Teacher90 {
public:int operator()(double a ,string s) {cout << "operator()(double a ,string s) called a = " << a <<" s = "<<s << endl;return 111;}int operator()() {cout << "operator()called " << endl;return 222;}
};void main() {//2.1通过匿名对象调用Teacher90()();Teacher90()(89,"nihao");//2.2Teacher90 tea;tea();tea(90,"haode");//2.3tea.operator()();//等价于 tea();tea.operator()(80,"sdf"); //等价于 tea.(80,"sdf");}
3. 可被转换为函数指针的类对象
//3. 可被转换为函数指针的类对象
class Teacher91 {
public:using tfpoint = void(*)(int);//1.定义一个函数指针类型,operator tfpoint() {//2.将operator 和 函数指针 结合使用,这里的含义是 opearator的返回值类型为Teacher91::tfpointreturn mysfunc; //3. 需要return一个 返回值类型为Teacher91::tfpoint 的函数指针类型,因此要找一个函数返回,该函数的返回值是void,参数是int的//return NULL; 如果return NULL 会有run time exception}static void mysfunc(int tv) {cout << "Teacher91::mysfunc() 静态成员函数执行了 tv = " << tv << endl;}
};void main() {Teacher91 tea;tea(90);//先调用tfpoint()函数,再调用mysfunc函数,等价于tea.operator Teacher91::mysfunc()(90);
}
4. 类成员函数指针
注意写法有点不同
//4. 类成员函数指针
class Teacher92 {
public:void func(int vt) {cout << "void func(int vt) call vt = " << vt << endl;}
};void main() {//方式1void (Teacher92::*pointfunc)(int) = &Teacher92::func;Teacher92 tea;(tea.*pointfunc)(89);//方式2void (Teacher92::*pointfunc1)(int);pointfunc1 = &Teacher92::func;Teacher92 tea1;(tea1.*pointfunc1)(99989);
}
5.总结
二 std::function(可调用对象包装器)
如何把上述4种不同的可调用对象的形式统一一下,统一的目的是方便咱们调用。
就要用到 function 可调用对象包装器。
要使用function 可调用对象包装器,先要include functional
#include <functional>
1. std::function 是一个类模版,用来装各种可调用对象,但是不能装类成员函数指针
std::function 类模版的特点:就是能够通过给它指定模版参数,它就能够用统一的方式来处理函数。
0.不能绑定类成员函数指针
1.绑定普通函数
//1.使用function 包装普通函数
void func100(int tv) {cout << "func100(int tv) called tv = " << tv << endl;
}void main() {function<void(int)> fu1 = func100;fu1(200);
}
2.绑定类的静态成员函数
//2.绑定类的静态成员函数
class Teacher93 {
public:static void teacher93staticfunc(int tv) {cout << "Teacher93::teacher93staticfunc(int tv) called tv = " << tv << endl;}
};void main() {function<void(int)> fu1 = Teacher93::teacher93staticfunc;fu1(900);
}
3.绑定仿函数
//3.绑定仿函数
class Teacher94 {
public:int operator()(double d, string s) {cout << "int operator()(double d, string s) called d = " << d << " s = " << s << endl;return 800;}
};void main() {Teacher94 tea;double dou = 89.8;string str = "nihao";function<int(double, string)> f1 = tea;//注意这里,直接将类对象 tea 直接赋值过去。f1(dou, str);
}
4.范例演示
范例1:
//4.范例演示
class Teacher95 {
private://成员变量为 function<void()> 类型std::function<void()> fcallback;public://再构造方法中,参数也是一个 std::function<void()> 类型的变量,//在初始化列表中,将形参f 直接赋值给 私有成员 fcallbackTeacher95(const std::function<void()> f):fcallback(f) {}public://再提供一个 函数,当调用这个函数的时候,直接调用 私有成员 fcallbackvoid runcallback() {fcallback();}};class Teacher96 {
public:void operator()() {cout << "Teacher96 的 operator() 函数被调用" << endl;}
};void main() {Teacher96 tea96;Teacher95 tea95(tea96);//tea95是需要一个 可调用对象 作为参数的,而tea96因为重写了operator函数,因此就是一个可调用对象tea95.runcallback();
}
范例2:
int func110(double d, string s) {cout << "func110(double d, string s) called d = " <<d <<" s = " <<s << endl;return 800;
}void func113(double dou ,string str, const std::function<int(double,string)> &f1) {f1(dou, str);
}void main() {func113(89.8,"nihao", func110);
}
三,std::bind绑定器
是个类模版 ,C++ 引入
从实际写的方法中,可以看到,这个类模版
#include <functional>
作用:
1.能够将对象以及相关的参数绑定在一起,绑定完后可以直接调用。
2.也可以用std::function进行保存,在需要的时候保存
格式:
std::bind(待绑定的函数对象,参数绑定值1,参数绑定值2......参数绑定值n)
总结:
a.将可调用对象和参数绑定在一起,所以可以直接调用
f | - | 可调用 (Callable) 对象(函数对象、指向函数指针、到函数引用、指向成员函数指针或指向数据成员指针) |
b.如果函数有多个参数,可以绑定一部分参数,其他参数在调用的时候指定。
返回值:
是一个可调用对象类型
1.将普通函数的对象和参数绑定
//1.能够将对象以及相关的参数绑定在一起,绑定完后可以直接调用。void func120(int x ,int y ,int z) {cout << "x = " << x << " y = " << y << " z = " << z << endl;
}//关于普通函数的绑定
void main() {//1直接匿名调用bind(func120, 10, 20, 30)();//2.使用bind 获得返回值,那么bind的返回值类型是啥呢?查看 C++文档,bind 绑定不同的类型,返回值也不同,长长一大堆,查看了文档给出来的例子,都是用auto,因此我们这里也用autoauto b1 = bind(func120, 100, 203, 302);b1();//3.上述我们都是将三个参数的值都写死了,如果我们只想绑定一部分参数,该怎么写呢?//3.1 让参数1 和参数2的值相等auto b2 = bind(func120, std::placeholders::_1, std::placeholders::_1, 898);b2(567);//3.2 让参数1 和参数2的值bu 相等auto b3 = bind(func120, std::placeholders::_1, std::placeholders::_2, 1234);b3(8978,678);//3.3 注意,我们这里是用b2再调用,但是仍然可以调用,只是第二个参数56789没有用b2(8978, 56789);//3.4 ,参考前面的,如果想要实参的第一个不发挥作用,则这么写auto b4 = bind(func120, std::placeholders::_2, std::placeholders::_2, 90);b4(8, 9);//解释:std::placeholders::_1, std::placeholders::_2都是占位符,表示的含义是:将来用实参的第一个参数,和 实参的第二个参数来替代我//由于bind 的返回值是一个可调用对象,结合前面学习的function,就可以使用function 将其保存起来}
2.将普通函数的对象和参数绑定 坑定1
bind对于预先绑定的函数参数 是通过值传递的,因此a 和 b 是值传递的
bind对于不事先 绑定的参数,通过std::placeholders传递的参数,真正调用的函数的 形参类型是啥,std::placeholders占位的实参的类型就是啥
void func121(int &x, int &y, int &z) {++x;++y;++z;cout << "func121 x = " << x << " y = " << y << " z = " << z << endl;
}void main() {int a = 10;int b = 20;int c = 30;auto b1 = bind(func121, a, b, std::placeholders::_1);b1(c);cout << "main a = " << a << " b = " << b << " c = " << c << endl;//结果:// func121 x = 11 y = 21 z = 31// main a = 10 b = 20 c = 31//这说明:bind对于预先绑定的函数参数 是通过值传递的,因此a 和 b 是值传递的// bind对于不事先 绑定的参数,通过std::placeholders传递的参数,真正调用的函数的 形参类型是啥,std::placeholders占位的实参的类型就是啥}
3.绑定 类的函数
注意调用方式,以及坑点。
调用方式:
bind(函数地址,类对象,参数一,参数二)
auto b1 = bind(&Teacher122::Teacher122func, tea, age, "nihao");
b1();
bind(函数地址,类对象引用,参数一,参数二)
auto b2 = bind(&Teacher122::Teacher122func, &tea, 678, "uio");
b2();
auto b3 = bind(&Teacher122::Teacher122func, &tea, std::placeholders::_1, std::placeholders::_2);
b3(56,"sss");
坑点:
第二个参数,传递类对象,实际上会调用 copy 构造函数,生成一个临时对象进去,那么参数都是临时对象的,返回值肯定可以临时对象的,因此在代码中真正改动的也是 临时对象的mage。
如果想要真正的改动类对象中的值,需要使用引用。
class Teacher122 {public:Teacher122() {cout << "Teacher122 构造函数called" << endl;}Teacher122(const Teacher122 & obj) {this->mage = obj.mage;cout << "Teacher122 copy 构造函数 called" << endl;}Teacher122 & operator=(Teacher122 & obj) {this->mage = obj.mage;cout << "Teacher122 operator= 函数called" << endl;return *this;}virtual ~Teacher122() {cout << "Teacher122 析构函数 被调用" << endl;}double Teacher122func(int dou,string str) {cout << "Teacher122func called dou = " << dou << " str = " << str << endl;this->mage = dou;return 3.1415926;}public :int mage;
};void main() {Teacher122 tea;int age = 980;auto b1 = bind(&Teacher122::Teacher122func, tea, age, "nihao");b1();//再还没有运行前,我们可以分析一下,age的值,会被赋值给tea.mage,因此mage的值 我们推导应该是980cout << "tea.mage = " << tea.mage << endl;//运行发现,tea.mage的值是一个随机值,这很诡异,明明给mage赋值了980呀。//添加 构造函数,copy构造函数,operator=函数,析构函数再次运行,发现有copy构造函数运行。//那么这个tea,在bind函数调用的时候,bind(&Teacher122::Teacher122func, tea, age, "nihao");是否copy了一份临时对象呢?//测试还真是的。//结论:bind 在对类成员函数的绑定的时候,如果传递的是类对象实例,实际上 会 传递的是临时对象: copy 构造函数会被调用//那么问题又来了,如果我们就是想要赋值mage的值,怎么办呢?//fix 方案,传递一个引用就好了auto b2 = bind(&Teacher122::Teacher122func, &tea, 678, "uio");b2();cout << "use 引用后的结果 tea.mage = " << tea.mage << endl;auto b3 = bind(&Teacher122::Teacher122func, &tea, std::placeholders::_1, std::placeholders::_2);b3(56,"sss");cout << "use 引用后的结果22 tea.mage = " << tea.mage << endl;cout << "duandian" << endl;//Teacher122 构造函数called//Teacher122 copy 构造函数 called//Teacher122func called dou = 980 str = nihao//tea.mage = -858993460//Teacher122func called dou = 678 str = uio//use 引用后的结果 tea.mage = 678//Teacher122func called dou = 56 str = sss//use 引用后的结果22 tea.mage = 56//duandian//Teacher122 析构函数 被调用//Teacher122 析构函数 被调用
}
4.bind 和function 配合使用
function 的作用是 :可调用对象包装器,意思就是将这些 可调用对象 包装起来
bind的作用是 : 将对象和参数绑定,返回一个 可调用对象。
发现了吗:因此bind的返回值,可以直接用function 包起来
//4.bind 和function 配合使用void func126(int vt) {cout << "func126(int vt) vt = " << vt << endl;
}void main() {auto b1 = bind(func126, std::placeholders::_1);function<void(int)> f1 = b1;f1(11);
}
5. bind 还能绑定 成员变量,绑定完成后还能放在 function 中。感觉这个没啥用呀,记录一下。
注意写法。
注意引用。
class Teacher127 {
public:int mage;
};void main() {Teacher127 tea127;auto b1 = bind(&Teacher127::mage, &tea127);b1() = 90;cout << tea127.mage << endl;function<int &()> f1 = b1;f1() = 89090;cout << tea127.mage << endl;
}
6. bind 绑定 类对象,该类对象需要实现 operator()的重写。
class Teacher128 {
public:int mage;public:void operator()(int a ) {cout << "Teacher128 operator()(int a ) called a =" << a << endl;}
};void main() {Teacher128 tea;auto b1 = bind(tea, std::placeholders::_1);b1(89);
}
四 总结
bind的思想:所谓的延迟调用,将可调用对象统一格式,保存起来,需要的额时候再调用。
function 绑定一个可调用对象,但是类成员函数不能绑定。
std::bind则可以将类成员函数,类成员变量都能绑。