回调函数
回调函数的创建步骤大概为:
- 声明一个函数指针类型。
- 拟写使用回调函数的函数,将函数指针类型及变量名声明作为参数传递。
- 拟写符合函数指针类型的实现函数,将实现函数的指针作为参数传递给使用它的函数。
定义回调函数的指针类型,包括返回值类型、(*类型名)函数指针、参数表
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
可调用对象:
- 一个函数指针
- 一个具有operator()成员函数的类对象(传说中的仿函数),lambda表达式
- 一个可被转换为函数指针的类对象
- 一个类成员(函数)指针
- bind表达式或其它函数对象
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 <functional>
#include <iostream>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);// 存储 lambdastd::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 <functional>
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 <functional>
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::bind
是一个基于模板的函数,作用是绑定并返回一个 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的作用就是转换函数签名,将缺少的参数补上,将多了的参数去掉,甚至还可以交换原来函数参数的位置。
注意
- 调用指向非静态成员函数指针或指向非静态数据成员指针时,首参数必须是引用或指针(可以包含智能指针,如 std::shared_ptr 与std::unique_ptr),指向将访问其成员的对象。
- 到 bind 的参数被复制或移动,而且决不按引用传递,除非包装于 std::ref 或 std::cref 。
- 允许同一 bind 表达式中的多重占位符(例如多个 _1 ),但结果仅若对应参数( u1 )是左值或不可移动右值才良好定义。
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;
}
参考
c++ 回调函数与std::function使用实例