C++11中bind绑定器和function函数对象介绍
作者:菜鸟~~
一. 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)); }
到此这篇关于C++11中bind绑定器和function函数对象介绍的文章就介绍到这了,更多相关C++bind绑定器内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!