C++bind绑定器
菜鸟~~ 人气:0一. bind1st和bind2nd
1.C++ STL中的绑定器
- bind1st:operator()的第一个形参变量绑定成一个确定的值
- bind2nd:operator()的第二个形参变量绑定成一个确定的值
C++11从Boost库中引入了bind绑定器和function函数对象机制
bind可用于给多元函数降元:Bind + 二元函数对象 = 一元函数对象
#include<iostream> #include<vector> #include<functional> #include<algorithm>//泛型算法 #include<ctime> using namespace std; template<typename Container> void showContainer(Container& con) { //typename Container::iterator it=con.begin(); auto it = con.begin(); for (; it != con.end(); ++it) { cout << *it << " "; } cout << endl; } int main() { vector<int> vec; srand(time(nullptr)); for (int i = 0; i < 20; ++i) { vec.push_back(rand() % 100 + 1); } showContainer(vec); sort(vec.begin(), vec.end());//默认从小到大排序 showContainer(vec); //greater需要二元函数对象 sort(vec.begin(), vec.end(), greater<int>());//从大到小排序 showContainer(vec); /* 把70按顺序插入到vec容器中 ->找第一个小于70的数字 operator()(const T &val) greater a>b less a<b 绑定器+二元函数对象=》一元函数对象 bind1st:+greater bool operator()(70,const_Ty&_Right) bind2nd:+less bool operator()(const_Ty &_Left,70) */ auto it1 = find_if(vec.begin(), vec.end(), bind1st(greater<int>(), 70)); if (it1 != vec.end()) { vec.insert(it1, 70); } showContainer(vec); return 0; }
2.bind1st和bind2nd的底层原理实现
绑定器本身是一个函数对象
#include<iostream> #include<vector> #include<functional> #include<algorithm> #include<ctime> using namespace std; template<typename Container> void showContainer(Container& con) { auto it = con.begin(); for (; it != con.end(); ++it) { cout << *it << " "; } cout << endl; } //遍历两个迭代器之间的元素,如果满足函数对象的运算,就返回当前的迭代器,如果都不满足就返回end template<typename Iterator,typename Compare> Iterator my_find_if(Iterator first, Iterator last, Compare comp) { //这里传入的comp是封装好的一元函数对象 for (; first != last; ++first) { if (comp(*first))//获取容器的一个元素 { return first; } } return last; } template<typename Compare,typename T> class _mybind1st//绑定器是函数对象的一个应用 { public: //这里传入的comp是二元函数对象 _mybind1st(Compare comp,T val) :_comp(comp),_val(val){} //通过重载operator()把二元函数对象封装为一元函数对象 bool operator()(const T& second) { return _comp(_val, second); } private: Compare _comp; T _val; }; template<typename Compare,typename T> _mybind1st<Compare, T> mybind1st(Compare comp, const T& val) { //直接使用函数模板,好处是可以进行类型的推演 //这里传入的comp是一个二元函数对象 //通过二元函数对象构造一元函数对象 //绑定器本身是一个函数对象,也就是重载了operator() return _mybind1st<Compare, T>(comp, val); } int main() { vector<int> vec; srand(time(nullptr)); for (int i = 0; i < 20; ++i) { vec.push_back(rand() % 100 + 1); } showContainer(vec); sort(vec.begin(), vec.end());//默认从小到大排序 showContainer(vec); //greater需要二元函数对象 sort(vec.begin(), vec.end(), greater<int>());//从大到小排序 showContainer(vec); auto it1 = my_find_if(vec.begin(), vec.end(), mybind1st(greater<int>(), 70)); if (it1 != vec.end()) { vec.insert(it1, 70); } showContainer(vec); return 0; }
二. 模板的完全特例化和非完全特例化
有完全特例化优先匹配完全特例化,有部分特例化就匹配部分特例化,没有的话就从原模板自己实例化
#include<iostream> using namespace std; template<typename T> class Vector { public: Vector() { cout << "call Vector template init" << endl; } }; //对char*类型提供完全特例化版本 template<> class Vector<char*> { public: Vector() { cout << "call Vector<char*> init" << endl; } }; //对指针类型提供的部分特例化版本(部分:只知道是个指针,但是指针的类型是什么不知道) template<typename Ty> class Vector<Ty*> { public: Vector() { cout << "call Vector<Ty*> 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) { return a + b; } int main() { Vector<int> vec1; Vector<char*> vec2; Vector<int*> vec3; Vector<int(*)(int, int)> vec4; Vector<int(int, int)> vec5; //注意区分函数类型和函数指针类型 typedef int(*PFUNC1)(int, int); PFUNC1 pfunc1 = sum; cout << pfunc1(10, 20) << endl; typedef int PFUNC2(int, int); PFUNC2* pfunc2 = sum; cout << (*pfunc2)(10, 20) << endl; return 0; }
#include<iostream> #include<typeinfo> using namespace std; //T包含了所有大的类型 template<typename T> void func(T a) { cout << typeid(T).name() << endl; } int sum(int a, int b) { return a + b; } //把所有形参类型都取出来 template<typename R, typename A1, typename A2> void func2(R(*a)(A1, A2)) { cout << typeid(R).name() << endl; cout << typeid(A1).name() << endl; cout << typeid(A2).name() << endl; } template<typename R,typename T,typename A1,typename A2> void func3(R(T::*a)(A1, A2)) { cout << typeid(R).name() << endl; cout << typeid(T).name() << endl; cout << typeid(A1).name() << endl; cout << typeid(A2).name() << endl; } class Test { public: int sum(int a, int b) { return a + b; } }; int main() { //func(10);//int //func("aaa");//const char * func(sum); func2(sum); func3(&Test::sum); return 0; }
三. function函数对象
绑定器,函数对象,lambda表达式本质上都是函数对象,只能使用在一条语句中,但是如果想要在多条语句中使用,就需要function
使用function函数需要注意:
- 用函数类型实例化function;
- 通过function调用
operator()
函数的时候,需要根据函数类型传入相应的参数。
#include<iostream> #include<functional> using namespace std; void hello1() { cout << "hello world!" << endl; } void hello2(string str) { cout << str << endl; } int sum(int a, int b) { return a + b; } int main() { //从function的模板定义处,看到希望用一个函数类型实例化function function<void()> func1 = hello1; func1();//func1.operator() => hello1() function<void(string)> func2 = hello2; func2("hello hello2!"); function<int(int, int)> func3 = sum; cout << func3(2, 3) << endl; function<int(int, int)> func4 = [](int a, int b)->int {return a + b; }; cout << func4(3, 4) << endl; return 0; }
function不仅可以留下普通全局函数的类型,对于类的成员方法也可以进行类型保留:
#include<iostream> #include<functional> using namespace std; class Test { public://必须依赖一个对象void(Test::*pfunc)(string) void hello(string str) { cout << str << endl; } }; int main() { //成员方法一经编译都会多一个当前类型的this指针 function<void (Test*, string)> func = &Test::hello; Test t; //对于成员方法的调用需要依赖一个成员对象 func(&t, "call Test::hello!"); return 0; }
function的特点:可以把所有函数、绑定器、函数对象和lambda表达式的类型保留起来,在其他地方都可以使用。否则绑定器、lambda表达式就只能使用在语句中。
#include<iostream> #include<functional> #include<map> using namespace std; 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; //使用function函数对象完成 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 << "------------------" << endl; cout << "1.查看所有书籍信息" << endl; cout << "2.借书" << endl; cout << "3.还书" << endl; cout << "4.查询书籍" << endl; cout << "5.注销" << endl; cout << "------------------" << endl; cout << "请选择:"; cin >> choice; auto it = actionMap.find(choice); if (it == actionMap.end()) { cout << "输入数字无效,重新选择" << endl; } else { it->second(); } //不好,因为这块代码无法闭合,无法做到“开-闭”原则,也就是说这块代码随着需求的更改需要一直改,永远也闭合不了,避免不了要产生很多问题 /* switch(choice) { case 1:break; case 2:break; case 3:break; case 4:break; case 5:break; default:break; } */ } return 0; }
function的实现原理:
#include<iostream> #include<functional> using namespace std; void hello(string str) { cout << str << endl; } int sum(int a, int b) { return a + b; } template<typename Fty> class myfunction{}; /* template<typename R,typename A1> class myfunction<R(A1)> { public: //typedef R(*PFUNC)(A1); using PFUNC = R(*)(A1); myfunction(PFUNC pfunc):_pfunc(pfunc){} R operator()(A1 arg) { return _pfunc(arg); } private: PFUNC _pfunc; }; template<typename R, typename A1,typename A2> class myfunction<R(A1,A2)> { public: //typedef R(*PFUNC)(A1); using PFUNC = R(*)(A1,A2); myfunction(PFUNC pfunc) :_pfunc(pfunc) {} R operator()(A1 arg1,A2 arg2) { return _pfunc(arg1,arg2); } private: PFUNC _pfunc; }; */ //...表示可变参,A表示的是一组1类型,个数任意 template<typename R, typename... A> class myfunction<R(A...)> { public: using PFUNC = R(*)(A...); myfunction(PFUNC pfunc) :_pfunc(pfunc) {} R operator()(A... arg) { return _pfunc(arg...); } private: PFUNC _pfunc; }; int main() { myfunction<void(string)> func1(hello); func1("hello world"); myfunction<int(int, int)> func2(sum); cout << func2(10, 20) << endl; return 0; }
四. bind和function实现线程池
#include<iostream> #include<functional> using namespace std; using namespace placeholders; //C++11 bind 绑定器=>返回的结果是一个函数对象 void hello(string str) { cout << str << endl; } int sum(int a, int b) { return a + b; } class Test { public: int sum(int a, int b) { return a + b; } }; int main() { //bind是函数模板,可以自动推演模板类型参数 bind(hello, "Hello bind!")(); cout << bind(sum, 20, 30)() << endl; cout << bind(&Test::sum, Test(), 20, 30)() << endl; //function只接受一个类型,绑定器可以给相应的函数绑定固定的参数,绑定器只能使用在语句当中 //参数占位符,绑定器出了语句,无法继续使用 bind(hello, _1)("hello bind 2"); cout << bind(sum, _1, _2)(20, 30) << endl; //此处把bind返回的绑定器binder就复用起来了 function<void(string)> func1 = bind(hello, _1); func1("hello china!"); func1("hello shan xi!"); func1("hello da li!"); }
#include<iostream> #include<functional> #include<thread> #include<vector> using namespace std; using namespace placeholders; //线程类 class Thread { public: Thread(function<void(int)> func,int no):_func(func),_no(no){} thread start() { thread t(_func,_no); return t; } private: function<void(int)> _func; int _no; }; //线程池类 class ThreadPool { public: ThreadPool(){} ~ThreadPool() { //释放thread对象占用的堆资源 for (int i = 0; i < _pool.size(); i++) { delete _pool[i]; } } //开启线程池 void startPool(int size) { for (int i = 0; i < size; i++) { //不管是C++里面的thread还是Linux里面的pthread需要的线程函数都是一个C函数,是不能够使用成员方法的,因为它是C的函数类型,不可能把成员方法的函数指针给一个C的函数指针,接收不了。所以就需要绑定,把runInThread所依赖的参数全部绑定 _pool.push_back(new Thread(bind(&ThreadPool::runInThread, this, _1),i)); } for (int i = 0; i < size; i++) { _handler.push_back(_pool[i]->start()); } for (thread& t : _handler) { t.join(); } } private: vector<Thread*> _pool; vector<thread> _handler; //把runInThread这个成员方法充当线程函数 void runInThread(int id) { cout << "call runInThread! id:" << id << endl; } }; int main() { ThreadPool pool; pool.startPool(10); return 0; }
五. lambda表达式
- 函数对象的应用:使用在泛型算法参数传递、比较性质、自定义操作、优先级队列和智能指针
- 函数对象的缺点:需要先定义一个函数对象类型,但是类型定义完后可能只是用在了定义的地方,后面可能不会再用了,没有必要为了需要一个函数对象定义一个类型,这个类型就永远在代码当中。
C++11函数对象的升级版 => lambda表达式:
- lambda表达式:底层依赖函数对象的机制实现的。
- lambda表达式语法:[捕获外部变量](形参列表) ->返回值{操作代码};
如果lambda表达式的返回值不需要,那么“->返回值”可以省略
[捕获外部变量]
- [ ]:表示不捕获任何外部变量
- [=]:以传值的方式捕获外部的所有变量
- [&]:以传引用的方式捕获外部的所有变量[this]:捕获外部的this指针
- [=,&a]:以传值的方式捕获外部的所有变量,但是a变量以传引用的方式捕获
- [a,b]:以传值的方式捕获外部变量a和b
- [a,&b]:a以值传递捕获,b以传引用的方式捕获
1.lambda表达式的实现原理
#include<iostream> using namespace std; template<typename T=void> class TestLambda01 { public: void operator()() { cout << "hello world" << endl; } }; template<typename T = int> class TestLambda02 { public: TestLambda02() {} int operator()(int a, int b) { return a + b; } }; template<typename T = int> class TestLambda03 { public: TestLambda03(int a,int b):ma(a),mb(b){} void operator()()const { int tmp = ma; ma = mb; mb = tmp; } private: mutable int ma; mutable int mb; }; class TestLambda04 { public: TestLambda04(int &a,int &b):ma(a),mb(b){} void operator()()const { int tmp = ma; ma = mb; mb = tmp; } private: int& ma; int& mb; }; int main() { auto func1 = []()->void {cout << "hello world" << endl; }; func1(); auto func2 = [](int a, int b)->int {return a + b; }; cout << func2(20, 30) << endl; int a = 10; int b = 20; //按值传递a,b值未被改变 auto func3 = [a, b]()mutable { int tmp = a; a = b; b = tmp; }; func3(); cout << "a:" << a << " b:" << b << endl; //传引用值a,b值被改变 auto func4 = [&]() { int tmp = a; a = b; b = tmp; }; func4(); cout << "a:" << a << " b:" << b << endl; cout << "--------------------" << endl; TestLambda01<> t1; t1(); TestLambda02<> t2; cout << t2(20, 30) << endl; TestLambda03<> t3(a,b); t3(); cout << "a:" << a << " b:" << b << endl; TestLambda04 t4(a,b); t4(); cout << "a:" << a << " b:" << b << endl; return 0; }
mutable
:成员变量本身也不是常量,只不过在常方法中this指针被修饰成const,在声明成员变量前加mutable
,可以在const
方法中修改普通的成员变量
lambda表达式后面修饰mutable相当于在它的所有成员变量添加一个mutable修饰。
2.lambda表达式的应用实践
lambda表达式应用于泛型算法:
#include<iostream> #include<vector> #include<algorithm> using namespace std; int main() { vector<int> vec; for (int i = 0; i < 20; ++i) { vec.push_back(rand() % 100 + 1); } sort(vec.begin(), vec.end(), [](int a, int b)->bool { return a > b; }); for (int val : vec) { cout << val << " "; } cout << endl; //65按序插入序列 要找一个小于65的数字 auto it = find_if(vec.begin(), vec.end(), [](int a)->bool {return a < 65; }); if (it != vec.end()) { vec.insert(it, 65); } for (int val : vec) { cout << val << " "; } cout << endl; for_each(vec.begin(), vec.end(), [](int a) { if (a % 2 == 0) cout << a << " "; }); cout << endl; return 0; }
既然lambda表达式只能使用在语句中,如果想跨语句使用之前定义好的lambda表达式,采用function类型来表示函数对象的类型。
哈希表的应用:
#include<iostream> #include<vector> #include<map> #include<functional> using namespace std; int main() { //auto只能出现在根据右边表达式推导左边的类型,只能使用在函数的局部作用域的范围之内 //预先lambda表达式不知道需要先存储lambda表达式类型 map<int, function<int(int, int)>> caculateMap; 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 << "请选择"; int choice; cin >> choice; cout << caculateMap[choice](10, 15) << endl; return 0; }
智能指针自定义删除器:
#include<iostream> #include<vector> #include<functional> using namespace std; int main() { unique_ptr<FILE, function<void(FILE*)>> ptr1(fopen("data.txt", "w"), [](FILE* pf) {fclose(pf); }); }
传入函数对象使得容器元素按照指定方式排列:
#include<iostream> #include<vector> #include<functional> #include <queue> using namespace std; class Data { public: Data(int val1=10,int val2=10):ma(val1),mb(val2){} int ma; int mb; }; int main() { //优先级队列 //priority_queue<Data> queue; using FUNC = function<bool(Data&, Data&)>; priority_queue<Data, vector<Data>, FUNC> maxHeap([](Data& d1, Data& d2)->bool { return d1.mb > d2.mb; }); maxHeap.push(Data(10, 20)); maxHeap.push(Data(15, 15)); maxHeap.push(Data(20, 10)); }
加载全部内容