设计模式原则
bind、function机制
概述
- bind和function 极常用!!
- bind1st:operator()的第一个形参绑定成一个确定的值
- bind2nd:operator()的第二个形参绑定成一个确定的值
- c++11从boost库引入了bind绑定器和function函数对象机制
- lambda表达式:底层依赖函数对象的机制实现
bind1st、bind2nd(弃用)。bind更好用
1 | int main() |
原理
- bind1st底层原理
- bind1st -> _bind1st
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83//
// Created by dell on 2022/3/18.
// 手写bind1st、bind2nd、my_find_if
template<typename Container>
void showContainer(Container & con)
{
// Container作为一个模板类型参数,当编译器遇到Container::mem这样的代码时,他不会知道mem是一个类型成员还是一个static数据成员,
// 直至实例化时才会知道。但是,为了处理模板,编译器必须知道名字是否表示一个类型。
// 默认情况下,C++假定通过作用域运算符::访问的名字不是类型。因此,如果我们希望使用一个模板类型参数的类型成员,就必须显示告诉编译器改名字是一个=类型。我们通过使用typename来实现这一点。
// typename:明确指出iterator是一个类型而非static数据成员。
typename Container::iterator iter = con.begin();
for(;iter!=con.end();++iter)
{
cout<<*iter<<" ";
}
cout<<endl;
}
// 函数模板怎么匹配的???Iterator和谁匹配??
// 函数模板可以进行类型推演:根据我们传入的实参,可以把模板类型推演出来
template<typename Iterator,typename Compare>
Iterator my_find_if(Iterator first,Iterator last,Compare cmp)
{
for( ; first!=last;++first)
{
if(cmp(*first))
{
return first;
}
}
return first;
}
// 重载(),真正的一元函数对象
// 封装,真正做事情的还是那个二元函数
template<typename Compare,typename T>
class _mybind1st
{
public:
_mybind1st(Compare cmp,const T& t):_cmp(cmp),_val(t){}
bool operator()(const T& second) // 要传入的参数(非绑定参数)
{
return _cmp(_val,second); // 绑定了第一个参数的二元函数
}
private:
Compare _cmp; // 二元函数
T _val; // 绑定的那个参数
};
// 函数模板:封装了一元对象的产生。
// 就是个中转站。传入函数和要绑定的参数。返回绑定完的一元函数对象。
// 函数模板作用:让用户无需指明类,直接传参数即可。如果直接用_mybind1st的话,则需要自己指明模板参数类型
// 使用函数模板:好处是可以进行类型推演。根据我们传入的实参,可以把模板类型推演出来。
template<typename Compare,typename T>
_mybind1st<Compare,T> mybind1st(Compare cmp,const T& val)
{
return _mybind1st<Compare,T>(cmp,val); // 生成一元函数对象
}
int main()
{
vector<int> v;
srand(time(nullptr));
for(int i=0;i<10;++i)
{
v.push_back(rand()%100);
}
showContainer(v);
sort(v.begin(),v.end(),greater<int>());
showContainer(v);
vector<int>::iterator iter = my_find_if(v.begin(),v.end(), mybind1st(greater<int>(),70));
cout<<*iter<<endl;
}
58 82 80 86 74 69 79 22 68 60
86 82 80 79 74 69 68 60 58 22
86 82 80 79 74 69 68 60 58 22
69
- bind1st -> _bind1st
function
用途:建立函数表
- 把所用的函数、绑定器、函数对象、lambda表达式的类型表达起来,否则绑定器和lambda表达式只能存在于语句之中,在其他语句还要重写绑定器和lambda表达式。
- 作用:建立函数表。switch不满足开闭原则,我们建立
map<int,function<void(void)>>
函数表来替代1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
#include<unordered_map>
void doShow(){cout<<"查看所有书籍"<<endl;}
void doBorrow(){cout<<"借书"<<endl;}
void doBack(){cout<<"还书"<<endl;}
int main()
{
//如果用函数指针如 void(*)(void) 则second只能接受普通的全局函数。不能接受函数对象(如bind绑定器返回的函数对象;如函数对象类(仿函数)的对象)
unordered_map<int,function<void(void)>> actionMap;
actionMap.insert(std::make_pair(1,doShow));
actionMap.insert({2,doBack});
actionMap.insert({3,doBorrow});
while(1)
{
int choice;
cout<<"choice is"<<endl;
cin>>choice;
if(actionMap.find(choice)!=actionMap.end())
{
actionMap[choice]();
}
else
{
cout<<"no choice"<<endl;
}
}
}
“渔夫在无法捕鱼时,就会修补他的网,在这段看似停滞不前的日子里,你可以休息但不要什么都不做”
完全特例化和部分特例化
必须先有个泛化的,然后才能有部分和全部特例化
泛化、特例化
1
2
3
4
5
6
7
8
9
10
11
12
13// 泛化
template<typename T>
class B { }
// 就相当于 template<typename T> class B<T>{}
template<typename R,typename A>
class B<R(A)> { }
// class A<内容> <>中的内容,就是这个类在实例化时,<>钟要填入的东西的类型!
// 通过我们填入的东西,解析出R、A这两个模板参数分别是什么
// 如 B<int(int)> b(show) 就是 R:int 。A:int
int show(int a){ cout<<a<<endl;}例子
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
/*
* 完全特例化和部分特例化
*/
#include<iostream>
using namespace std;
#if 0
template<typename Ty>
class Vector
{
public:
Vector(){cout<<"class Vector"<<endl;}
};
// 针对指针提供的部分特例化
template<typename Ty>
class Vector<Ty*>
{
public:
Vector(){cout<<"class Vector<Ty*>"<<endl;}
};
// 针对函数指针(有返回值,两个形参变量)提供的部分特例化
// 这三个合起来共同定义了一个类型 R(*)(A1,A2)
template<typename R,typename A1,typename A2>
class Vector<R(*)(A1,A2)>
{
public:
Vector(){cout<<"class Vector<R(*)(A1,A2)"<<endl;}
};
// 针对char*提供的完全特例化
template<>
class Vector<char*>
{
public:
Vector(){cout<<"class Vector<char*>"<<endl;}
};
// 针对函数提供的部分特例化
template<typename Ty,typename R1,typename R2>
class Vector<Ty(R1,R2)>
{
public:
Vector(){cout<<"class Vector<Ty(R1,R2)>"<<endl;}
};
int main()
{
Vector<int> v1;
Vector<int*> v2;
Vector<char*> v3;
Vector<int(*)(int,int)> v4;
Vector<int(int,int)> v5;
}
#endif
E:\Code\CLionCode\project02\cmake-build-debug\project02.exe
class Vector
class Vector<Ty*>
class Vector<char*>
class Vector<R(*)(A1,A2)
class Vector<Ty(R1,R2)>
1 | gcc就是那样的,只输出类型名的第一个字符,要输出完整的名字可以这样: |
模板实参推演
- 非常非常强大的功能
- 将一个大类型(一个函数模板参数) 使用多个模板参数 拆分成多个小类型。便于使用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57int sum(int a,int b){}
class Test
{
public:
int sum(int a,int b){}
};
/*
* 模板的实参推演 =》基本概念很简单
*/
// T包含了所有的大的类型,返回值,所有形参的类型都取出来。
template<typename T >
void func(T t)
{
cout<<abi::__cxa_demangle(typeid(t).name(),0,0,0 )<<endl;
}
// 将类型拆开 即将上面的 函数指针类型T 拆成 返回类型R 参数类型A1 A2组成的
template<typename R,typename A1,typename A2>
void func2(R(*a)(A1,A2))
{
cout<<abi::__cxa_demangle(typeid(R).name(),0,0,0 )<<endl;
cout<<abi::__cxa_demangle(typeid(A1).name(),0,0,0 )<<endl;
cout<<abi::__cxa_demangle(typeid(A2).name(),0,0,0 )<<endl;
}
// 返回值 类类型 参数类型
template<typename R,typename T,typename A1,typename A2>
void func3(R(T::*t)(A1,A2)) // T:: 。怎么写,看typeid.name()就知道了
{
cout<<abi::__cxa_demangle(typeid(R).name(),0,0,0 )<<endl;
cout<<abi::__cxa_demangle(typeid(T).name(),0,0,0 )<<endl;
cout<<abi::__cxa_demangle(typeid(A1).name(),0,0,0 )<<endl;
cout<<abi::__cxa_demangle(typeid(A2).name(),0,0,0 )<<endl;
}
int main()
{
func(sum);
func2(sum);
cout<<endl;
func(&Test::sum);
func3(&Test::sum);
}
int (*)(int, int)
int
int
int
int (Test::*)(int, int)
int
Test
int
int
function底层原理
1 |
|
bind
bind例子
- bind参数?
std::bind(待绑定的函数对象/函数指针/成员函数指针,参数绑定值1,参数绑定值2,...,参数绑定值n);
- 参数一:函数对象/函数指针。(如果是临时对象的话,则不需要取地址)
- 其余参数:函数列表中的参数
- 如果有不想绑定的参数,那么需要用
placeholder::_x
来占位,不能略过!! - hh1 ;hh2
bind的第一个参数是待绑定的函数对象或者函数指针,之后跟随多个参数以设定待绑定函数的参数绑定方式。待绑定函数有多少个参数,则bind后便需要多少个参数以一一声明其参数的绑定方法.当参数绑定为某一固定值时,则其对应参数绑定值可以使一个变量或常量.当需要将参数与绑定所生成的函数对象的某个参数相关联时,则需要用到在标准中预定义的几个常量_1、_2、_3等.这些常量声明在std::placeholders命名空间内.
1
2
3function<bool(int)> f = bind(greater<int>(),placeholders::_1,2);
cout<<f(4)<<endl; // 1
cout<<f(1)<<endl; // 0
1 | #include<iostream> |
bind 和 function 实现简易线程池
1 | #include<thread> |
lambda表达式(很强大)
- 生成函数对象的新方式
- [捕获外部变量](形参列表)->返回值{操作代码}
- 如果返回值=void,那么”->void”可以省略
- [捕获外部变量]
- []:表示不捕获任何外部变量
- [=]:以传值的方式捕获外部的所有变量
- [&]:以传引用的方式捕获外部的所有变量
- [this]:捕获外部this指针
- [=,&a]:以传值的方式捕获外部的所有变量,但以传引用的方式捕获a变量
- [a,b]:以值传递的方式捕获外部变量a和b
- [a,&b]:a以传值的方式捕获,b以传引用的方式捕获
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105//
// Created by dell on 2022/3/20.
//
/*
* 函数对象升级 -> lambda表达式
* 函数对象缺点:
* 使用在泛型算法参数传递,比较/自定义操作,优先级队列,智能指针删除器
* lambda语法:
* [捕获外部变量](形参列表)->返回值{操作代码}
*
* 如果返回值=void,那么"->void"可以省略
* [捕获外部变量]
* []:表示不捕获任何外部变量
* [=]:以传值的方式捕获外部的所有变量
* [&]:以传引用的方式捕获外部的所有变量
* [this]:捕获外部this指针
* [=,&a]:以传值的方式捕获外部的所有变量,但以传引用的方式捕获a变量
* [a,b]:以值传递的方式捕获外部变量a和b
* [a,&b]:a以传值的方式捕获,b以传引用的方式捕获
*/
#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;
template<typename T = int>
class TestLambda
{
public:
TestLambda(){}
T operator()(T a,T b) const
{
return a + b;
}
};
int main()
{
vector<int> vec;
for(int i=0;i<10;++i)
{
vec.push_back(rand()%100);
}
// lambda生成函数对象传入泛型算法!这样就不用再去记忆greater,less。也不用bind绑定器绑定greater/less生成函数对象
sort(vec.begin(),vec.end(),
[](int a,int b)->bool{
return a>b;
});
for_each(vec.begin(),vec.end(),[](int a)->void{cout<<a<<" ";});
cout<<endl;
// 问题解决了:应该怎么判断传入什么lambda参数()?
// 就和给find_if传递普通的函数对象一样,函数对象所需要的参数 应当就是*迭代器
// 因为find_if 中遍历会用到 FUNC(*迭代器) FUNC就是我门传入的函数对象
// 这里lambda的(参数) 应当和find_if要求传入的函数对象的参数相同
// 这个参数find_if语法上没法限制
// 不过我们应该传入的参数是 find_if遍历时 遍历到的 " *迭代器 "
vector<int>::iterator iter = find_if(vec.begin(),vec.end(),
[](int a)->bool{return a<70;});
vec.insert(iter,70);
for_each(vec.begin(),vec.end(),[](int a)->void{cout<<a<<" ";});
cout<<endl;
return 0;
}
#if 0
int main()
{
//
TestLambda<> t;
cout<<t(1,2)<<endl;
// lambda表达式生成函数对象,替代我们写的模板函数对象类(冗余代码)
auto t2 = [](int a,int b)->int{return a + b;};
cout<<t2(1,2)<<endl;
int x = 4,y = 5;
auto t3 = [x,y] () mutable {
int t = x;
x = y;
y = t;
// 如果没加mutable的话
// Cannot assign to a variable captured by copy in a non-mutable lambda
// 不能在非可变lambda里修改按值捕获的变量
};
t3();
cout<<x<<" "<<y<<endl;
auto t4 = [&x,&y] () {
int t = x;
x = y;
y = t;
};
t4();
cout<<x<<" "<<y<<endl;
}
#endif
function配合lambda使用
- 应该用什么类型来接收lamdba表达式返回的函数对象?/ 如何跨语句使用lambda表达式
- 当然是
function
- 当然是
- 例子
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
721、函数表
unordered_map<int,function<int(int,int)>> f;
f[0] = [](int a ,int b)->int{return a+b;};
cout<<f[0](1,2)<<endl;
2、删除器
// 智能指针自定义删除器
unique_ptr<FILE*,function<void(FILE*)>> up1(
(fopen("a.txt", "w")),
[](FILE* pf)->void{ fclose(pf);}
);
3. 容器比较规则
// 灵活设定规则。并且可以对同一种对象进行不同规则比较
class Data
{
public:
Data(int a):m_a(a){}
// bool operator< (const Data& rd) const // 如果自定义的话,必须是const函数。因为容器默认调用的比较函数对象的参数列表会用常量引用。而常量引用对象只能调用常量函数
// {
// return m_a<rd.m_a;
// }
int getData() const
{
return m_a;
}
private:
int m_a;
};
int main()
{
// 采用function 配合 lambda表达式
// 更加灵活的定义比较规则。不用在类内内置<号或者>号。
using FUNC = function<bool(const Data& rd1,const Data& rd2)>;
priority_queue<Data,vector<Data>,FUNC> q(
[](const Data& rd1,const Data& rd2)->bool{
return rd1.getData() < rd2.getData();
});
q.push(Data(10));
q.push(Data(20));
}
template<typename _Tp, typename _Sequence = vector<_Tp>,
typename _Compare = less<typename _Sequence::value_type> >
class priority_queue{
...
typedef typename _Sequence::value_type value_type;
priority_queue()
: c(), comp() { }
explicit
priority_queue(const _Compare& __x, _Sequence&& __s = _Sequence())
: c(std::move(__s)), comp(__x)
{ std::make_heap(c.begin(), c.end(), comp); }
protected:
// See queue::c for notes on these names.
_Sequence c;
_Compare comp; // _comp为函数对象 _Compare为函数对象类
}
// less源码:所以自定义<必须为const函数
template<typename _Tp>
struct less : public binary_function<_Tp, _Tp, bool>
{
_GLIBCXX14_CONSTEXPR
bool
operator()(const _Tp& __x, const _Tp& __y) const
{ return __x < __y; }
};
ref
ReStart
test02
如何插入图片
test
这是摘要