概述
包装器可以对一个可调用对象进行包装
可调用对象包括
- lambda表达式
- 仿函数
- 函数指针
使用包装器可以避免模板实例化出多份代码,可以提高效率
function
function的使用
function可以使用构造函数进行初始化,也能用赋值进行初始化
function< ( …)>:function括号里面就是包装的函数的类型对应的参数类型,括号外面就是函数的返回值
仿函数
struct Functor
{
double operator()(double i)
{
return i / 2;
}
};
int main()
{
double m=10.1;
function<double(double)> f2 = Functor(); //仿函数对象
cout<<f2(m)<<endl;
return 0;
}
Lambda表达式
int main()
{
double m=10.1;
function<double(double)> f2 = [](double i){
return i/2;};//Lambda表达式
cout<<f2(m)<<endl;
return 0;
}
函数指针
void l(int& x)
{
x+=2;
cout<<"222"<<endl;
}
void demo4()
{
int x=2;
function<void(int&)> s=l;
s(x);
cout<<x<<endl;
// cout<<ret<<endl;
}
类成员函数
class Plus
{
public:
static double fI(double x)
{
return x;
}
double f(double i)
{
return i / 2;
}
};
int main()
{
//只要是成员函数就要加&
function<double(double)> f3 = &Plus::fI; //静态的成员函数
//还可以包装成员函数
cout << f2(10) << endl;
//对于非静态的成员函数
function<double(Plus, double)> ff = &Plus::f; //非静态的成员函数要多加一个Plus,因为有this指针
cout << ff(Plus(), 123) << endl;//使用的时候要多添加一个类对象(可以是匿名对象)
return 0;
}
function的实战
逆波兰表达式
根据 逆波兰表示法,求表达式的值。
有效的算符包括 +、-、、/ 。每个运算对象可以是整数,也可以是另一个逆波兰表达式。
注意 两个整数之间的除法只保留整数部分。
可以保证给定的逆波兰表达式总是有效的。换句话说,表达式总会得出有效数值且不存在除数为 0 的情况。
示例 1:
输入:tokens = [“2”,“1”,“+”,“3”,"“]
输出:9
解释:该算式转化为常见的中缀算术表达式为:((2 + 1) * 3) = 9
示例 2:
输入:tokens = [“4”,“13”,“5”,”/“,”+“]
输出:6
解释:该算式转化为常见的中缀算术表达式为:(4 + (13 / 5)) = 6
示例 3:
输入:tokens = [“10”,“6”,“9”,“3”,”+“,”-11",““,”/“,””,“17”,“+”,“5”,“+”]
输出:22
解释:该算式转化为常见的中缀算术表达式为:
((10 * (6 / ((9 + 3) * -11))) + 17) + 5
= ((10 * (6 / (12 * -11))) + 17) + 5
= ((10 * (6 / -132)) + 17) + 5
= ((10 * 0) + 17) + 5
= (0 + 17) + 5
= 17 + 5
= 22
题解:如果是操作数就入栈,如果是操作符,就把栈顶的两个数取出来进行处理
我们可以使用map,命令和函数可以进行映射,映射到的为function,因为function对于函数指针,lambda表达式,仿函数都可以包装,所以就可以了,一个命令对应一个函数
class Solution
{
public:
int evalRPN(vector<string> &tokens)
{
//使用列表初始化
//包装器,只要是可调用对象就行了,很方便
//使用包装器之后就能用函数映射了
map<string, function<int(int, int)>> opMapFunc; //动作和函数映射的map
//命令增加了只需要加这个map这里,一个命令对应一个函数
opMapFunc["+"] = [](int a, int b)
{
return a + b; }; //包装器,初始化用函数指针,仿函数,lambda表达式
opMapFunc["-"] = [](int a, int b)
{
return a - b; }; //包装器,初始化用函数指针,仿函数,lambda表达式
opMapFunc["*"] = [](int a, int b)
{
return a * b; }; //包装器,初始化用函数指针,仿函数,lambda表达式
opMapFunc["/"] = [](int a, int b)
{
return a / b; }; //包装器,初始化用函数指针,仿函数,lambda表达式
//也可以用列表初始化
stack<int> s;
int i = 0;
int top;
//逆波兰表达式,一个命令和一个函数的映射关系就可以用包装器来解决
for (int i = 0; i < tokens.size(); i++)
{
string &str = tokens[i]; //用别名弄,减少了拷贝
// str为操作数
// map里面为操作数就找不到
if (opMapFunc.find(str) == opMapFunc.end())
{
//说明就是操作数
s.push(stoi(str)); //入栈
}
else
{
//这里就是操作符
int left = s.top();
s.pop();
int right = s.top();
s.pop();
//取出来了数据
s.push(opMapFunc[str](left, right));
}
}
return s.top();
}
};
bind
- bind可以对function绑定的函数调整参数的顺序
- bind可以调整参数的个数(比如将包装之后的函数固定一个参数为某个东西,就不需要我们再手动去添加)
// bind也是一个函数包装器
//接收一个可调用对象,生成一个可调用对象
int sub(int a, int b)
{
return a - b;
}
class subber
{
public:
int Sub(int a, int b)
{
return a - b;
}
};
void demo3()
{
function<int(int, int)> f = sub;
cout << f(10, 3) << endl;
//调整参数的顺序
function<int(int, int)> f1 = bind(sub,placeholders::_1,placeholders::_2);//这个就是啥都没做
cout << f1(10, 3) << endl;
function<int(int, int)> f3 = bind(sub,placeholders::_2,placeholders::_1);//参数顺序调换了
//这里的_1就是把原来的第一个参数换到现在的位置,_2就是把原来的第2个参数换到现在的第一个
//这个就是可以把库里面的用的不习惯的函数,切换参数顺序
//主要是通过绑定来进行调整参数个数
cout << f3(10, 3) << endl;
function<int(subber,int, int)> f4 = &subber::Sub;
cout << f4(subber(),10, 3) << endl;//使用了用还要加一个对象
//每次都这样用很烦
function<int(int, int)> f5 = bind(&subber::Sub,subber(),placeholders::_1,placeholders::_2);//这样子进行绑定,第一个参数就绑死了,使用的时候就不需要再去添加
cout << f5(10, 3) << endl;
//假如说第一个参数都是一样的
function<int(int)> f6=bind(&subber::Sub,subber(),100,placeholders::_1);//第一个参数都是100
cout<<f6(20)<<endl;
//这里也能用auto进行接收
}
class A{
public:
A()=default;
void l(int &x)
{
x += 2;
cout << "222" << endl;
}
void ll(int &x)
{
x += 2;
cout << "222" << endl;
}
};
int main()
{
map<int, function<void(int &)>> lll = {
{
X, bind(&A::ll,A(), placeholders::_1)},
{
2, bind(&A::l,A() ,placeholders::_1)}};
lll[X](m);//映射包装器与bind,可以使得我们在用的时候,对类立案的参数就不要写了
return 0;
}