目录
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();
}
}