详解bind绑定器和function函数对象机制

目录

bind1st和bind2nd使用代码举例如下:

自己动手实现bind1st和find_if:

bind和function机制

模板的完全特例化(全特化)和部分特例化(偏特化)

区分一下函数类型和函数指针类型:

通过vector演示模板特例化的使用:

自己动手实现function 理解原理:

bind绑定器使用举例:

muduo源码中的线程池用C++代码模拟:

lambda表达式的原理和实践 :

通过lambda表达式实现有自定义优先级队列的比较方式;


bind1st:把函数对象的operator ()的第一个形参变量绑定成一个确定的值;

bind2nd:把函数对象的operator()的第二个形参变量绑定成一个确定的值;

bind1st和bind2nd使用代码举例如下:

template <typename  Container>
void showContainer(Container con){
    typename Container::iterator it = con.begin();
    for(;it!=con.end();++it){
        cout<<*it<<" ";
    }
    cout<<endl;
}
int main(){
    srand(time(nullptr));
    vector<int> vec;
    for(int i =0;i<20;++i){
        vec.push_back(rand()%100 +1);
    }
    sort(vec.begin(),vec.end()); //默认从小到大排序
    showContainer(vec);
    sort(vec.begin(),vec.end(),std::greater<int>());
//从大到小排序,通过greater函数对象比较
    //greater二元函数对象:不管什么排序都是二个元素进行比较;
    showContainer(vec);
    //把70按顺序插入vec容器中:那就是找第一个小于70的数字位置,把70插入到这个位置
    auto it1 = find_if(vec.begin(),vec.end(),bind1st(std::greater<int>(),70));
    //找到了就返回那个迭代器,否则返回end()尾后迭代器
    cout<<*it1<<endl;
    auto it2 = find_if(vec.begin(),vec.end(),bind2nd(std::less<int>(),70));
    cout<<*it2<<endl;
//find_if需要一元函数对象,所以把greater通过bind1st 或者less通过bind2nd 包装成一元函数对象传进去
    //greater : a>b    less:a < b
    //绑定器+二元函数对象->一元函数对象

    return 0;
}

自己动手实现bind1st和find_if:

template  <typename  Iterator ,typename Compare>
Iterator my_find_if(Iterator first,Iterator last,Compare comp){
    for(;first!=last;++first){
        if(comp(*first)){ //comp.operator()(*first)
            return first;
        }
    }
    return last;
}
template <typename  Compare,typename  T>
class _mybind1st{
private:
    Compare _comp;
    T _val;
public:
    _mybind1st(Compare comp,T val):_comp(comp),_val(val){}
    bool operator()(const T& second){
        return _comp(_val,second);
        //通过绑定器实现的一元函数对象底层还是靠二元函数对象完成比较
    }
};
//mybind1st(greater<int>(),70)
template <typename  Compare ,typename  T>
_mybind1st<Compare,T> mybind1St(Compare comp,const T& val){
    //通过函数模板封装了一元函数对象的产生,省去我们自己写模板类型参数了
    //函数模板:通过实参自动进行类型推导
    return _mybind1st<Compare,T>(comp,val);
}

bind和function机制

C++11从boost库中引入了bind绑定器 和 function函数对象机制;绑定器将参数绑定到函数对象后还是函数对象;

void hello1() {
//函数类型:void();  函数指针类型:void(*)()
    cout << "hello world" << endl;
}
void hello2(string str) {  
//函数类型:void(string); 函数指针类型:void(*)(string)
    cout << str << endl;
}
//全局函数和成员函数不一样,全局函数直接通过函数名可以直接调用,而成员函数的调用依赖对象;
class Test {
public:
	//void (Test::*pfunc)(string ) ,pfunc是指向成员函数的指针
	void hello(string string1) { cout << string1 << endl; }
	//注意成员函数有一个隐藏的形参:this指针
	//函数类型:void (Test*,string)
};
int main() {
/*function:  绑定器、lambda等本质上都是函数对象,它们只能在一条语句中使用,如果想要在多条语句中使用呢? 我们看到functon源码中他的模板类型参数名称是Fty,表示它想让你在实例化这个类模板的时候用一个函数类型;
注意区分:《函数类型和函数指针类型是二个不同的东西》
如:void(*)() 这是个指针类型,表示指向返回值是void不带形参的一个函数
而实例化function用函数类型,只给出返回值和参数列表就可以了 */

#if 0
	function<void()> func1 = hello1;
//用函数对象类型把函数类型保留下来了
//func1.operator(); 里面调用包装的 hello1()函数
	func1();
	function<void(string)> func2 = hello2;
	func2("hello cpp"); //func2.operator()(string str)  -> hello2(str);
//func2包装的函数有参数,所以需要传参
	function<int(int, int)> func4 = [](int a, int b)->int {return a + b; };
	func4(10, 20);
#endif
	function<void(Test*, string)> func5 = &Test::hello;
//注意成员函数hello的类型是:void(Test*,string) ,成员函数前面有个隐藏形参this指针
	func5(&Test(), "call Test::hello");
	//1.用函数类型实例化function;
	//2.通过function调用operator()函数的时候,需要根据函数类型传入相应的参数;
	//function这个函数对象类型非常好用,他可以把函数类型直接保留;
	return  0;
}

那么为什么要使用function呢,看下面例子:

void doShowAllBooks(){cout<<"查看所有书籍信息"<<endl;}
void doBorrow(){cout<<"借书"<<endl;}
void doBack(){cout<<"还书"<<endl;}
void doQueryBooks(){cout<<"查询书籍"<<endl;}
void doLoginOut(){cout<<"注销"<<endl;}
int main(){
    int choice = 0;
    unordered_map<int,function<void()>> actionMap;
    actionMap.insert({1,doShowAllBooks});
    actionMap.insert({2,doBorrow});
    actionMap.insert({3,doBack});
    actionMap.insert({4,doQueryBooks});
    actionMap.insert({5,doLoginOut});
    for(;;){
        cout<<"1.查看所有书籍信息"<<endl;
        cout<<"2.借书"<<endl;
        cout<<"3.还书"<<endl;
        cout<<"4.查询书籍"<<endl;
        cout<<"5.注销"<<endl;
        cin>>choice;
        auto it = actionMap.find(choice);
        if(it == actionMap.end()){
            cout<<"输入无效,从新输入"<<endl;
        }else{
            it->second();
        }
    }
#if 0 //下面设计不好,不符合开闭原则,不方便后序增加和删除等维护;
    switch (choice ) {
        case 1:..
        case 2:..
    }
#endif
    return  0;
}

模板的完全特例化(全特化)和部分特例化(偏特化)

简单的关于函数模板的完全特例化的例子:为什么需要特例化?

template <typename  T> //函数模板
bool compare(T a, T b) {
	cout << "template comapre" << endl;
	return a > b;
}
template <> //函数模板的完全特例化
bool compare<const char*>(const char* a, const  char* b) {
	cout << "compare<const char *>" << endl;
	return strcmp(a, b) > 0;
}
int main() {
	compare(10, 20); //函数模板实参推导:通过函数实参的类型推导出形参的类型;
	//从原模板实例化出一个处理推导出的类型的函数进行编译;

	compare("aaa", "bbb"); 
/*实参推导类型是const char * ;而在compare中比较的是地址,而不是真是字符串进行字典序比较,即
默认的函数模板的行为不符合我们的要求,因此我们需要针对cosnt char * 生成一个特例化版本的compaere函数 */

	return 0;
}

区分一下函数类型和函数指针类型:

//函数指针类型:
using PFUNC1 = int(*)(int ,int ); 
//using定义函数指针类型等价于下面的typedef定义函数指针类型
typedef int(*PFUNC2)(int,int );
PFUNC1  pfunc1 = sum;
PFUNC2  pfunc2 = sum;
pfunc1(1,2);
pfunc2(1,2);
//函数类型:
using FUNC1 = int(int,int);
typedef int (FUNC2)(int,int );
FUNC1 * pfunc3 = sum;
FUNC2 * pfunc4 = sum;
pfunc3(1,2);
pfunc4(1,2);
//decltype对函数名得到的是函数类型,要定义函数指针需要加上*
decltype(sum)* p = sum;
p(1,2);

通过vector演示模板特例化的使用:

template <typename  T>
class Vector{
public:
    Vector(){cout<<"call Vector template init"<<endl;}
};
template <>
class Vector<char*>{ //类模板关于char*类型的特例化版本
public:
    Vector(){cout<<"call Vector<char*>  init"<<endl;}
};
//现在想如果是指针类型,需要对Vector进行特例化,怎么做呢?
template  <typename  Ty>
class Vector<Ty*>{ //针对指针类型提供的部分特例化版本
public:
    Vector(){cout<<"call Vector<T*>  init"<<endl;}
};
template <typename  R,typename  A1,typename  A2>
class Vector<R(*)(A1,A2)>{ 
//针对函数指针(有返回值,有二个形参变量)提供的部分特例化版本
public:
    Vector(){cout<<"call Vector<R(*)(A1,A2) init"<<endl;}
};
template <typename  R,typename  A1,typename  A2>
class Vector<R(A1,A2)> { 
//针对函数类型(有返回值,有二个形参变量)提供的部分特例化版本
public:
    Vector(){cout<<"call Vector<R(A1,A2)>) init"<<endl;}

};
int sum(int a,int b){cout<<"sum(int a,int b)"<<endl;}
int main(){
    //匹配选择:1.完全特例化 2.部分特例化 3.原类模板
    Vector<int> vec1; //
    Vector<char*> vec2;
    Vector<int*> vec3;
    Vector<int(*)(int,int)> vec4; //里面是函数指针类型
    Vector<int(int,int )> vec5; //里面是函数类型
    return 0;
}

自己动手实现function 理解原理:

template <typename  R,typename  A1>
class myfunction<R(A1)>{ //完全特例化版本
private:
    using PFUNC= R(*)(A1); //函数指针类型,指向给构造函数传递的函数
    PFUNC  _pfunc;
public:
    myfunction(PFUNC pfunc):_pfunc(pfunc){}
    R operator ()(A1 arg){return _pfunc(arg);}
};
template  <typename  R,typename  A1,typename  A2>
class myfunction<R(A1,A2)>{//完全特例化版本
private:
    using PFUNC = R(*)(A1,A2);
    PFUNC  _pfunc;
public:
    myfunction(PFUNC pfunc):_pfunc(pfunc){}
    R operator ()(A1 arg1,A2 arg2){
        return _pfunc(arg1,arg2);
    }
};
//但是因为函数的版本多种多样,我们不可能实现所有的函数版本的特例化,那怎么办呢?
//可以使用可变模板参数
template  <typename  R,typename  ...A> //返回类型R确定,参数类型有很多个
class myfunction<R(A...)>{
private:
    using PFUNC = R(*)(A...);
    PFUNC _pfunc;
public:
    myfunction(PFUNC pfunc):_pfunc(pfunc){}
    R operator ()(A... arg){return _pfunc(arg...)};
};

bind绑定器使用举例:

bind绑定器返回的结果还是函数对象;

function是类模板,bind是函数模板;

bind绑定器可以给相应的函数,绑定固定的参数,而且发现这些绑定器只能使用在语句中,语句结束了,如果还想使用绑定器就得把绑定器类型留下来,就得综合使用bind和function;

bind的参数占位符std::placeholders_1 : 绑定器最多可以绑定20个参数

绑定器std::bind是最常用的函数适配器,它可以将函数对象的参数绑定至特定的值。对于没有绑定的参数可以使用std::placeholers::_1, std::placeholers::_2等标记。

void hello(string string1){cout<<string1<<endl;}
int sum (int a,int b){return a+b;}
class Test{
public:
    int sum (int a,int b){return a+b;}
};
int main(){
    auto binder = bind(hello,"hello world" );
    cout<<typeid(binder).name()<<endl;
    binder();
    cout<<bind(&Test::sum,Test(),10,20)()<<endl;
    bind(hello,std::placeholders::_1)("hello world"); 
//hello函数需要一个参数,但是参数具体是什么交给用户传递,这给先给参数占位符;
    cout<<bind(sum,std::placeholders::_1,std::placeholders::_2)(20,10)<<endl;
//绑定全局函数
    cout<<bind(&Test::sum,Test(),std::placeholders::_1,std::placeholders::_2)(10,20)<<endl;
//绑定类成员函数;注意类成员函数需要类对象才能调用,所以这里传入一个临时对象
//可以发现绑定器出了语句无法继续使用,可以通过fucntion把bind返回的函数对象保留下来
    function<void(string)> func1 =  bind(hello,std::placeholders::_1);
    func1("hello china");
    return  0;
}

muduo源码中的线程池用C++代码模拟:

bind绑定器和function函数对象机制 可以让类的成员函数用作线程函数

class Thread { //线程类
public:
	Thread(function<void(int )> func,int no) :_func(func),_no(no) {}
	thread start() {
		thread t(_func,_no); //底层调用_func(no);
		return t;
	}
private:
	function<void(int )>  _func; //线程函数对象;
	int _no;

};
class ThreadPool { //线程池类
public:
	ThreadPool() {}
	~ThreadPool() {
		for (int i = 0; i < _pool.size(); ++i) {
			delete _pool[i]; //释放Thread占用的堆资源
		}
	}
	//创建线程池,启动所有线程
	void startPool(int size) {
		//1.创建线程,通过bind和function实现成员函数作为线程函数
		for (int i = 0; i < size; ++i) {
			_pool.push_back(
				new Thread(bind(&ThreadPool::runInThread, this, 
                                std::placeholders::_1),i)//把线程id通过i传进去
				//bind绑定了this和参数i,返回函数对象充当线程函数
			);  //Thread需要线程函数
		}
		//2.启动线程
		for (int i = 0; i < size; ++i) {
			_handler.push_back(_pool[i]->start());
		}
		//3.等待所有子线程运行结束
		for (thread& t : _handler) {
			t.join();
		}
	}
private:
	void runInThread(int id) { //把成员函数充当线程函数需要bind和function配置使用;
		cout << "call runInThread! id " << id << endl;
	}
	//注意下面thread和Thread区别
	vector<thread> _handler; //所有线程
	vector<Thread*>  _pool; //_pool中是指向线程的指针
};
int main() {
	ThreadPool pool;
	pool.startPool(10);
	return 0;
}

lambda表达式的原理和实践 :

        函数对象的缺点:有时候只需要在一个地方使用函数对象,比如泛型算法参数传递、自定义优先级队列比较操作,但是却需要定义一个类出来,而这个类型生成的函数对象只被使用了一次,却永远的存在于代码中;所以在使用一次函数对象的地方直接使用lambda表达式就很方便;

        既然lambda表达式只能使用在语句当中,如果想跨语句使用之前定义好的lambda表达式怎么办?既然知道lambda表达式是函数对象,那么自然可以用function类型来表示它;

unordered_map<int,function<int(int,int )>> caculateMap;
//用function类型来表示函数对象lambda表达式的类型
caculateMap[1] = [](int a,int b)->int {return a+b;};
caculateMap[2] = [](int a,int b)->int {return a-b;};
caculateMap[3] = [](int a,int b)->int {return a*b;};
caculateMap[4] = [](int a,int b)->int {return a/b;};
cout<<"10+15:"<<caculateMap[1](10,15)<<endl;
cout<<"15-10:"<<caculateMap[2](15,10)<<endl;
cout<<"15*10:"<<caculateMap[3](15,10)<<endl;
cout<<"15/10:"<<caculateMap[4](15,10)<<endl;

通过lambda表达式用在智能指针自定义删除器上面,通过function得到类型:

//unique_ptr管理文件资源,默认删除是delete p不行,需要自定义删除器:FILE*pf, fclose(pf);
unique_ptr<FILE,function<void(FILE*)>> ptr1(fopen("data.txt","w"),
        [](FILE* pf)->void{cout<<"fclose(pf)"<<endl;fclose(pf);});

通过lambda表达式实现有自定义优先级队列的比较方式;

struct Data {
	int ma;
	int mb;
	friend	std::ostream& operator <<(std::ostream&, const Data&);
	Data(int a, int b) :ma(a), mb(b) {}
};
std::ostream& operator << (std::ostream& os, const Data& rhs) {
	os << "(" << rhs.ma << "," << rhs.mb << ")";
	return os;
}
int main() {
	//priority_queue<Data  >  queue1; //默认是大顶堆:默认用的less<T>
	using FUNC = function<bool(const Data&, const Data&)>;
	
	std::priority_queue<Data, vector<Data>, FUNC> minHeap(
		[](const Data & lhs, const Data & rhs) {
			return lhs.ma != rhs.ma ? lhs.ma > rhs.ma : rhs.mb >rhs.mb; 
		}
	);
	minHeap.push(Data(10, 20));
	minHeap.push(Data(20, 30));
	minHeap.push(Data(30, 40));
	minHeap.push(Data(40, 50));
	minHeap.push(Data(50, 60));
	while (!minHeap.empty())
	{
		Data val = minHeap.top();
		cout <<val<< endl;
		minHeap.pop();
	}
 }
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值