可调用对象
在C++中,可以像函数一样调用的有:普通函数、类的静态成员函数、仿函数、lambda函数、类的非静态成员函数、可被转换为函数的类的对象,统称可调用对象或函数对象。
可调用对象有类型,可以用指针存储它们的地址,可以被引用(类的成员函数除外)。
这里举几个例子
仿函数(本质是重载了()的类)
#include<iostream>
using namespace std;
struct Object
{
void operator()(int age, string name)
{
cout << "年龄:" << age << ",姓名:" << name << endl;
}
};
int main()
{
Object obj;
obj(20, "小谢");
Object& obj_r = obj; // 引用函数
obj_r(19, "小赵");
return 0;
}
lambda函数
#include<iostream>
using namespace std;
int main()
{
auto func = [](int age, string name)
{
cout << "年龄:" << age << ",姓名:" << name << endl;
};
func(20, "小谢");
auto& func_r = func;// 引用lambda对象。
func_r(19, "小赵");
return 0;
}
类的非静态成员函数
类的非静态成员函数有地址,但是,只能通过类的对象才能调用它,所以,C++对它做了特别处理。
类的非静态成员函数只有指针类型,没有引用类型,不能引用。
#include<iostream>
using namespace std;
struct Object
{
void show(int age, string name)
{
cout << "年龄:" << age << ",姓名:" << name << endl;
}
};
int main()
{
Object obj;
obj.show(20, "小谢");
void(Object::*pobj)(int, string) = &Object::show;// 定义类的成员函数的指针。
(obj.*pobj)(19, "小赵");
using PFun = void(Object::*)(int, string);
PFun p_show = &Object::show;
(obj.*p_show)(18, "芜湖");
return 0;
}
在上面的例子中满足条件的这些可调用对象对应的类型被统称为可调用类型。C++ 中的可调用类型虽然具有比较统一的操作形式,但定义方式五花八门,这样在我们试图使用统一的方式保存,或者传递一个可调用对象时会十分繁琐。现在,C++11通过提供std::function 和 std::bind统一了可调用对象的各种操作。
包装器function
包含头文件:#include <functional>
std::function<返回值类型(参数类型列表)> diy_name = 可调用对象;
#include<iostream>
#include<functional>
using namespace std;
int add(int a, int b)
{
cout << a << "+" << b << "=" << a + b << endl;
return a + b;
}
class T
{
public:
static int sub(int a, int b)
{
cout << a << "*" << b << "=" << a * b << endl;
return a * b;
}
};
class T1
{
public:
int operator()(int a, int b)
{
cout << a << "-" << b << "=" << a - b << endl;
return a - b;
}
};
int main()
{
//std::function<返回值类型(参数类型列表)> diy_name = 可调用对象;
function<int(int, int)> f1 = add;
f1(1, 2);
function<int(int, int)> f2 = T::sub;
f2(2, 3);
T1 t;
function<int(int, int)> f3 = t;
f3(3, 4);
return 0;
}
通过测试代码可以得到结论:std::function 可以将可调用对象进行包装,得到一个统一的格式,包装完成得到的对象相当于一个函数指针,和函数指针的使用方式相同,通过包装器对象就可以完成对包装的函数的调用了。
作为回调函数使用
#include <iostream>
#include <functional>
using namespace std;
class A
{
public:
// 构造函数参数是一个包装器对象
A(const function<void()>& f) : callback(f)
{
}
void notify()
{
callback(); // 调用通过构造函数得到的函数指针
}
private:
function<void()> callback; //成员变量->包装器对象
};
class B
{
public:
void operator()()
{
cout << "!!!" << endl;
}
};
int main(void)
{
B b;
A a(b);
a.notify();
return 0;
}
绑定器bind
std::bind()模板函数是一个通用的函数适配器(绑定器),它用一个可调用对象及其参数,生成一个新的可调用对象,以适应模板。
函数原型
template< class Fx, class... Args >
function<> bind (Fx&& fx, Args&...args);
Fx:需要绑定的可调用对象
args:/*绑定参数列表,可以是左值、右值和参数占位符std::placeholders::_n,如果参数不是占位符,缺省为值传递,std:: ref(参数)则为引用传递。*/
std::bind()返回std::function的对象。
std::bind()的本质是仿函数。
// 绑定非类成员函数/变量
auto f = std::bind(可调用对象地址, 绑定的参数/占位符);
// 绑定类成员函/变量
auto f = std::bind(类函数/成员地址, 类实例对象地址, 绑定的参数/占位符);
类成员函数需要绑定该类的this指针 。
#include<iostream>
#include<functional>
using namespace std;
struct Object
{
void operator()(int age, string name)
{
cout << "年龄:" << age << ",姓名:" << name << endl;
}
void show(int age, string name)
{
cout << "年龄:" << age << ",姓名:" << name << endl;
}
};
int main()
{
Object obj;
function<void(int, string)> f1 = bind(Object(), placeholders::_1, placeholders::_2);
f1(20, "小谢");
auto func = [](int age, string name)
{
cout << "年龄:" << age << ",姓名:" << name << endl;
};
function<void(int, string)> f2 = bind(func, placeholders::_1, placeholders::_2);
f2(19, "小赵");
// 类成员函数需要绑定该类的this指针
Object obj1;
function<void(Object&, int, string)> f3
= bind(&Object::show, placeholders::_1, placeholders::_2, placeholders::_3);
f3(obj1,17,"张三");
//这里为了统一,将对象提前绑定
function<void(int, string)> f4
= bind(&Object::show, obj1, placeholders::_1, placeholders::_2);
f4(16, "李四");
return 0;
}
在用绑定器绑定类成员函数或者成员变量的时候需要将它们所属的实例对象一并传递到绑定器函数内部。
bind的应用
改变参数位置
例如函数需要一个int和string两个参数
auto f = bind(func, placeholders::_1, placeholders::_2);
第一个参数为int,第二个为string,但是如果第一个想第一个传入string,第二个传入int
auto f = bind(func, placeholders::_2, placeholders::_1);
改变参数个数
改变参数个数主要是为了统一,便于使用函数模板,例如上述例子的部分代码
Object obj1;
function<void(Object&, int, string)> f3
= bind(&Object::show, placeholders::_1, placeholders::_2, placeholders::_3);
f3(obj1,17,"张三");
//这里为了统一,将对象提前绑定
function<void(int, string)> f4
= bind(&Object::show, obj1, placeholders::_1, placeholders::_2);
f4(16, "李四");
这里采取的是提前绑定,将对象提前绑定。
设置类成员函数为回调函数
在讲bind时上面已演示!!