回调函数的创建步骤大概为:
定义回调函数的指针类型,包括返回值类型、(*类型名)函数指针、参数表
typedef int (*Calc)(int a, int b);
回调函数的使用者函数
int CalcValue(int a, int b, const Calc &func) {
return func(a, b);
}
符合函数指针类型的实现函数
int Add(int a, int b) {
return a + b;
}
实现函数的类型必须要和函数指针的类型声明一致,也就是返回值和参数表(个数、类型)要完全一致。
typedef int (*Calc)(int a, int b);
int CalcValue(int a, int b, const Calc &func) {
return func(a, b);
}
int Add(int a, int b) {
return a + b;
}
int main()
{
int a = 4;
int b = 6;
int c = CalcValue(a, b, Add);
std::cout << "c: " << c << std::endl;
return 0;
}
可调用对象:
std::function 是一个模板类。作用是对C++中的可调用对象进行包装,例如普通函数、成员函数、模板函数、静态函数、lambda表达式等。可以把std::function看做一个函数对象,用于表示函数这个抽象概念。std::function的实例可以存储、复制和调用任何可调用对象,存储的可调用对象称为std::function的目标,若std::function不含目标,则称它为空,调用空的std::function的目标会抛出std::bad_function_call异常。
最基本的作用是,简化调用的复杂程度,统一调用的方式。如果代码中混杂着大量普通函数、模板函数、lambda,使用 std::function 是比较适合的。
std::function<returnType(argType, argType,...)> func;
模板类当中对类型的声明方式是 < 返回值类型 ( 参数类型1, 参数类型2, …) >
使用场景:
std::function<void(int)> f; // 这里表示function的对象f的参数是int,返回值是void
#include
#include
struct Foo {
Foo(int num) : num_(num) {}
void print_add(int i) const { std::cout << num_ + i << '\n'; }
int num_;
};
void print_num(int i) { std::cout << i << '\n'; }
struct PrintNum {
void operator()(int i) const { std::cout << i << '\n'; }
};
int main() {
// 存储自由函数
std::function<void(int)> f_display = print_num;
f_display(-9);
// 存储 lambda
std::function<void()> f_display_42 = []() { print_num(42); };
f_display_42();
// 存储到 std::bind 调用的结果
std::function<void()> f_display_31337 = std::bind(print_num, 31337);
f_display_31337();
// 存储到成员函数的调用
std::function<void(const Foo&, int)> f_add_display = &Foo::print_add;
const Foo foo(314159);
f_add_display(foo, 1);
f_add_display(314159, 1);
// 存储到数据成员访问器的调用
std::function<int(Foo const&)> f_num = &Foo::num_;
std::cout << "num_: " << f_num(foo) << '\n';
// 存储到成员函数及对象的调用
using std::placeholders::_1;
std::function<void(int)> f_add_display2 = std::bind(&Foo::print_add, foo, _1);
f_add_display2(2);
// 存储到成员函数和对象指针的调用
std::function<void(int)> f_add_display3 = std::bind(&Foo::print_add, &foo, _1);
f_add_display3(3);
// 存储到函数对象的调用
std::function<void(int)> f_display_obj = PrintNum();
f_display_obj(18);
}
#include
class A
{
std::function<void()> callback_;
public:
A(const std::function<void()>& f) :callback_(f) {};
void notify(void)
{
callback_();
}
};
class Foo {
public:
void operator()(void)
{
std::cout << __FUNCTION__ << std::endl;
}
};
int main(void)
{
Foo foo;
A aa(foo);
aa.notify();
}
#include
void call_when_even(int x, const std::function<void(int)>& f)
{
if (!(x & 1))
{
f(x);
}
}
void output(int x)
{
std::cout << x << " ";
}
int main(void)
{
for (int i = 0; i < 10; ++i)
{
call_when_even(i, output);
}
std::cout << std::endl;
}
是一个基于模板的函数,作用是绑定并返回一个 std::function 对象。
那么什么是“绑定”?它本身作为延迟计算的思想的一种实现,作为一个调用过程当中的转发者而存在,返回一个 std::function 对象。
它与 std::function 不同的是,function 是模板类,bind 是模板函数,而 bind 返回的可调用对象可以直接给 function 进行包装并保存。
为什么要进行“包装”与“转发”呢?
首先,不规范的解释是,function 的作用是包装,它可以包装类成员函数,但却无法生成类成员函数的可调用对象。而 std::bind 则是可以生成。
因此,function 与 bind 结合后,便成为了 C++ 中类成员函数作为回调函数的一种规范的实现方式。
std::bind(&funcName, std::placeholders::_1, ...);
当用作普通函数的绑定时,第一个参数是可调用对象(普通函数、lambda等),而第二个参数开始对应可调用对象的参数表。
std::placeholders::_1 代表可调用对象的第一个参数,_2就代表第二个参数,依此类推。
当用作类成员函数的绑定时,第一个参数仍然是作为类成员的可调用对象引用,第二个参数则是对象的指针,而第三个参数开始对应可调用对象的参数表。同样使用 std::placeholders::_* 依次向后推。
因为类成员函数都有一个默认的参数,this,作为第一个参数,这就导致了类成员函数不能直接赋值给std::function,需要std::bind,简言之,std::bind的作用就是转换函数签名,将缺少的参数补上,将多了的参数去掉,甚至还可以交换原来函数参数的位置。
注意
class Baseclass
{
public:
int Add(int a, int b) { return a + b; };
};
int main()
{
int a = 1;
int b = 2;
std::shared_ptr<Baseclass> ptr_class = std::make_shared<Baseclass>();
std::function<int(int, int)> addFunc = std::bind(&Baseclass::Add, ptr_class, std::placeholders::_1, std::placeholders::_2);
int c = addFunc(a, b);
std::cout << "c: " << c << std::endl;
return 0;
}