本文共 5761 字,大约阅读时间需要 19 分钟。
C++中可调用对象的概念,有以下几种定义:
1) 是一个函数指针 2) 是一个具有operator()成员函数的类对象 3) 是一个可被转换为函数指针的类对象 4) 是一个类成员(函数)指针void fun(void){ //...}struct Foo{ void operator()(void){ //... }};struct Bar{ using fr_t = void (*)(void); static void fun(){ //... } operator fr_t(void){ return fun; }};struct A{ int _a; void mem_func(void){ //... }};int main(){ using FUN = void (*func_ptr)(void) ; // 1.函数指针 FUN func_ptr = fun; func_ptr(); Foo foo; foo(); // 2.仿函数 Bar bar; bar(); // 3.可被转换为函数指针的类对象 void(A::*mem_func_ptr)(void) // 4.类成员函数指针 = &A::mem_func; int A::*mem_obj_ptr // 类成员指针 = &A::_a; A aa; (aa.*mem_func_ptr)(); aa,.*mem_ob_ptr = 123;}
上述(func_ptr,foo,bar,mem_func_ptr,mem_obj_ptr)都被称作可调用对象。上述的可调用对象并没有函数类型或者函数引用类型(只有函数指针),这是因为函数类型不能直接用来做定义对象,而函数引用更像const函数指针。
std::function是可调用对象的包装器,它是一个类模板,可以容纳除了类成员(函数)指针之外的所有可调用对象。通过指定它的模板参数,它可以用统一的方式处理函数,函数对象,函数指针,并允许保存和延迟执它们。
#include#include void func(){ std::cout <<__FUNCTION__<< std::endl;}class Foo{ public: static int foo_func(int a){ std::cout << __FUNCTION__ << "(" << a << ") ->:\n"; return a; }};class Bar{ public: int operator()(int a){ std::cout << __FUNCTION__ <<"("<< a <<") ->\n"; return a; }};int main(){ std::function fr1 = func; //绑定普通函数 fr1(); std::function fr2 = Foo::foo_func; //绑定类静态成员函数 fr2(123); Bar bar; std::function fr3 = bar; //绑定一个仿函数 fr3(321);}
从上述看到std::function的使用方法,当我们给std::function填入一个合适的函数签名(即一个函数类型,只需要返回值和参数表)之后,它就可以变成了一个容纳所有这类函数方式的”函数包装器“。
class A{ std::functioncallback; public: A(const std::function & f):callback(f){} void notify(){ callback();//回调到上层 }};class Foo{ public: void operator()(void){ std::cout <<__FUNCTION__<<"is executable"<< std::endl; }};int main(){ Foo foo; A aa(foo); aa.notify();}
从上面的例子中可以看到,std::function可以取代函数指针的作用。因为它可以保存函数延迟执行,所以适合做回调函数。
typedef void( *Func)(void);using Func = void(*)(void);
使用函数指针类型作为成员也可以完成,但是回调函数只能是普通函数而不能是仿函数等(需要再次转型)。
void call_when_even(int x,const std::function& f){ if(!(x & 1)){ f(x); } else{ std::cout <<"并未触发回调:"< << std::endl; }}void output(int x){ std::cout <<__FUNCTION__<<" parament value : "< << std::endl;}int main(){ int num = 10; while(num--) call_when_even(num,output);}
从上述例子可以看出,std::function比函数指针更加灵活。
std::bind用来可将于可调用对象与其参数一起绑定,绑定后的结果使用std::function进行保存,并延迟到任何我们需要的时候。
通俗讲,它有两大作用: 1)将可调用对象与其参数一起绑定为一个仿函数。 2)将多元(参数n,n>1)可调用对象转化成一元或者(n-1)元可调用对象,即只绑定部分参数。#include#include void output(int x,int y){ std::cout <<__FUNCTION__< <<","< << std::endl;}int main(){ std::bind(output,1,2)(); //输出:1,2 std::bind(output,std::placeholders::_1,2)(11); //输出:11,2 std::bind(output,2,std::placeholders::_1)(111);//输出:2,111 //std::bind(output,std::placeholders::_1,std::placeholders::_2)(1); //error std::bind(output,std::placeholders::_1,std::placeholders::_2)(100,1000);}
从上述可得对std::bind的返回结果直接调用。可以看到,std::bind可以绑定所有参数,也可以仅仅绑定部分参数。
class A{ public: int i_ = 100; void output(int x,int y){ std::cout <<__FUNCTION__<<":"<<<","< << std::endl; }};int main(){ A a; std::cout < << std::endl; std::function fr = std::bind(&A::output,&a,std::placeholders::_1,std::placeholders::_2); fr(1,2); std::function fr_i = std::bind(&A::i_,&a); fr_i() = 123; std::cout < << std::endl;}
通过std::function和std::bind的配合,对所有可调用对象均有了统一调用方法。
//查找元素大于10的元素个数int count = std::count_if(cool.begin(),cool.end(),std::bind1st(std::less (),10);//等与上式子int count = std::count_if(cool.begin(),cool.end(),std::bind2nd(std::greater (),10));//查找元素小于10的元素个数int count = std::count_if(cool.begin(),cool.end(),std::bind1st(std::greater (),10));//等于上式子int count = std::count_if(cool.begin(),cool.end(),std::bind2nd(std::less (),10));//查找元素大于10的元素个数using std::placeholders::_1;int count = std::count_if(cool.begin(),cool.end(),std::bind(std::less (),10,_1));//查找元素小于10的元素个数int count = std::count_if(cool.begin(),cool.end(),std::bind(std::less ,_1,10));
std::placeholders 占位符合理使用,就不要纠结使用bind1st还是bind2nd。
//判断大于5小于10的元素(左开右闭)auto f = std::bind(std::logical_and(), std::bind(std::greater (),std::placeholders::_1,5), std::bind(std::less_equal (),std::placeholders::_1,10));int count = std::count_if(cool.begin(),cool.end(),f);
typedef std::functionREC; //回调函数类型Type func(int ,int,REC);
在某个类中存在函数func,需要参数int,int,REC。于是在调用时可能如下:
bool awk(cosnt char *str,int b);func(10,10,awk);
但实际这样的函数在进行测试不能解决问题(需要重载或者修改原函数接口,而这么做不符合软件的开闭原则),因而需要使用lambda或者std::bind或者函数对象。
//std::bindbool sawk(std::string &s,const char* str,int b);std::string s;auto t1 = std::bind(sawk,std::ref(s),std::placeholders::_1,std::placeholders::_2);func(2,1,t1);// lambdastd::string s;auto t2 = [&s](const char*,int b)->bool {return true;};func(2,1,t2);//function objectclass TS{public: bool operator()(const char *str,int a);private: std::string d;};TS t3;func(2,1,std::ref(t3));
通过传入std::bind绑定的函数,lambda,以及仿函数得到的函数符合传入REC的类型,但是可以额外附带的参数可以完成测试功能。
转载地址:http://ocxgj.baihongyu.com/