文章目录
- 包装器
- 1、functional
- 2、绑定
这一篇比较简短,只是因为后要写异常和智能指针,所以就把它单独放在了一篇博客,后面新开几篇博客来写异常和智能指针
包装器
1、functional
包装器是一个类模板,对可调用对象类型进行再封装适配,可调用对象,比如函数指针,lambda等。包装器的头文件是functional。
template <class T> function;
template <class Ret, class... Args>
class function<Ret(Args...)>
模板参数说明:
Ret:被调用函数的返回类型
Args...:被调用函数的形参
实际使用
int f(int a, int b)
{
cout << "f" << endl;
return a + b;
}
struct Functor
{
public:
int operator() (int a, int b)
{
cout << "Functor" << endl;
return a + b;
}
};
int main()
{
//int(*pf1)(int, int) = f;函数指针
function<int(int, int)> f1 = f;
function<int(int, int)> f2 = Functor();
function<int(int, int)> f3 = [](int a, int b) {
cout << "lambda" << endl;
return a + b;
};
cout << f1(1, 2) << endl;
cout << f2(10, 20) << endl;
cout << f3(100, 200) << endl;
return 0;
}
三个int,第一个是函数返回值类型,后两个是参数类型。包装起包装起来的就可以传给模板参数
map<string, function<int(int, int)>> opFuncMap;
opFuncMap["函数指针"] = f1;
opFuncMap["仿函数"] = Functor();
opFuncMap["lambda"] = [](int a, int b) {
cout << "lambda" << endl;
return a + b;
};
cout << opFuncMap["lambda"](1, 2) << endl;
看一个题
逆波兰表达式求值
给你一个字符串数组 tokens ,表示一个根据 逆波兰表示法 表示的算术表达式。
请你计算该表达式。返回一个表示表达式值的整数。
注意:
有效的算符为 ‘+’、‘-’、‘*’ 和 ‘/’ 。
每个操作数(运算对象)都可以是一个整数或者另一个表达式。
两个整数之间的除法总是 向零截断 。
表达式中不含除零运算。
输入是一个根据逆波兰表示法表示的算术表达式。
答案及所有中间计算结果可以用32位整数表示。
之前的写法
class Solution {
public:
int evalRPN(vector<string>& tokens) {
stack<int> st;
for(auto& str : tokens)
{
if(str == "+" || str == "-" || str == "/" || str == "*")
{
int right = st.top();
st.pop();
int left = st.top();
st.pop();
switch(str[0])
{
case '+':
st.push(left+right);
break;
case '-':
st.push(left-right);
break;
case '*':
st.push(left*right);
break;
case '/':
st.push(left/right);
break;
}
}
else
{
st.push(stoi(str));
}
}
return st.top();
}
};
用包装器后
class Solution {
public:
int evalRPN(vector<string>& tokens) {
stack<int> st;
map<string, function<int(int, int)>> opFuncMap =
{
{"+", [](int a, int b){return a + b; }},
{"-", [](int a, int b){return a - b; }},
{"*", [](int a, int b){return a * b; }},
{"/", [](int a, int b){return a / b; }}
};//这里就是map的初始化,用C++11的列表初始化
for(auto str : tokens)
{
if(opFuncMap.count(str))
{
int right = st.top();
st.pop();
int left = st.top();
st.pop();
st.push(opFuncMap[str](left, right));
}
else
{
st.push(stoi(str));
}
}
return st.top();
}
};
包装器也可以包装成员函数。
class Plus
{
public:
static int plus1(int a, int b)
{
return a + b;
}
double plus2(double a, double b)
{
return (a + b) * _rate;
}
private:
int _rate = 2;
};
int main()
{
class Plus
{
public:
Plus(int rate = 2)
:_rate(rate)
{}
static int plus1(int a, int b)
{
return a + b;
}
double plus2(double a, double b)
{
return (a + b) * _rate;
}
private:
int _rate = 2;
};
int main()
{
function<int(int, int)> f1 = Plus::plus1;
function<int(Plus, double, double)> f2 = &Plus::plus2;
cout << f1(1, 2) << endl;
cout << f2(Plus(), 20, 20) << endl;
Plus p1(3);
cout << f2(p1, 20, 20) << endl;
return 0;
}
静态成员函数可以直接调用,而非静态的需要在第一个位置加上类名,因为有this指针,然后后面的Plus前加上&,静态函数也可以加上这个&,使用这个函数的时候,非静态需要在第一个参数位置放上类的对象,可以是匿名对象,如果在声明f2时,传的是*Plus,那么下面调用时就必须传对象的地址,所以就不能传匿名对象的地址,因为右值无法取地址。
包装器本质上是仿函数,f1,f2,f3就是对象,然后调用operator(),传过去参数,然后operator()再去调用对应函数,传类的对象就用对象来调用,传指针就指针来调用。
2、绑定
绑定是一个函数模板,用来调整参数。绑定是一个通用的函数适配器,接受一个可调用对象,可调用对象就是三个,函数指针、lambda、仿函数,生成一个新的可调用对象来适配。
bind函数第一个参数是一个万能引用,左右值都可传,然后后面的是占位符,_1表示第一个参数,_2表示第二个参数,以此类推,这些占位符是一个placeholders空间里。
int Print(int a, int b)
{
cout << a << " ";
cout << b << endl;
}
int main()
{
Print(10, 20);
auto RP = bind(Print, placeholders::_2, placeholders::_1);
RP(10, 20);//再次调用就换了顺序了。
return 0;
}
如果bind写着_1在_2前面,那就没换顺序,要换顺序占位符就得对应着写。bind函数会返回一个对象,我们可以用auto来推演类型,还可以用function<void(int, int)>。实际调用的还是Print,不过适配器就是套了一个壳。
绑定真正有用的是改变参数个数
用这段代码做例子
class Sub
{
public:
Sub(int rate)
:_rate(rate)
{}
int func(int a, int b)
{
return (a - b) * _rate;
}
private:
int _rate;
};
class Solution {
public:
int evalRPN(vector<string>& tokens) {
stack<int> st;
map<string, function<int(int, int)>> opFuncMap =
{
{"+", [](int a, int b) {return a + b; }},
{"-", [](int a, int b) {return a - b; }},
{"*", [](int a, int b) {return a * b; }},
{"/", [](int a, int b) {return a / b; }}
};//这里就是map的初始化,用C++11的列表初始化
for (auto str : tokens)
{
if (opFuncMap.count(str))
{
int right = st.top();
st.pop();
int left = st.top();
st.pop();
st.push(opFuncMap[str](left, right));
}
else
{
st.push(stoi(str));
}
}
return st.top();
}
};
int main()
{
function<int(Sub, int, int)> fSub = &Sub::func;
fSub(Sub(1), 10, 20);
return 0;
}
这是上面包装器的写法。这样的写法无法给opFuncMap传fSub对象,因为参数个数不一致,这时候就是绑定的作用体现了。
function<int(int, int)> fSub = bind(&Sub::func, Sub(1), placeholders::_1, placeholders::_2);
fSub(10, 20);
把Sub(1)对象显式地传给func函数,顺序没有变,只是第一个参数显示传,剩下两个就从_1开始排顺序。也可以对其他参数来绑定。
function<int(int, int)> fSub = bind(&Sub::func, placeholders::_1, 10, placeholders::_2);
fSub(Sub(1), 20);
本篇gitee
结束。