目录
[2.1 静态成员函数](#2.1 静态成员函数)
[2.2 非静态成员函数](#2.2 非静态成员函数)
[3.1 调整参数顺序](#3.1 调整参数顺序)
[3.2 调整参数个数](#3.2 调整参数个数)
1、function包装器
包装器是用来包装可调用对象的,这里的可调用对象主要有函数指针、仿函数、lambda表达式
function本质是一个类模板,也是一个包装器,头文件是<functional>
function<返回值(参数列表)> f = 可调用对象
int f(int a, int b)
{
return a + b;
}
struct Functor
{
public:
int operator() (int a, int b)
{
return a + b;
}
};
int main()
{
// 包装可调用对象
function<int(int, int)> f1 = f;
function<int(int, int)> f2 = Functor();
function<int(int, int)> f3 = [](int a, int b) {return a + b; };
cout << f1(1, 2) << endl;
cout << f2(1, 2) << endl;
cout << f3(1, 2) << endl;
return 0;
}
优点是可以进行类型统一,就像我们玩游戏使用英雄释放技能时,按下一个技能的按键,英雄就会做出相应的动作,实际上,按下按键救会产生一个命令,命令通常是一个字符串,一般会将命令和函数进行映射。为了映射,此时可以使用一个map来完成映射,第一个参数存命令,第二个参数存这个命令对应的函数,但函数可能是函数指针、仿函数、lambda,lambda没有类型(底层有,但是同一个lambda在不同时间生成的类型都不一样),仿函数只能写死,函数指针可以,但太麻烦,所以就可以考虑使用包装器来进行统一
我们通过一个题目来看
这道题普通的做法是开一个栈,遍历数组,当遍历到的字符是数字时,就放入栈,当遍历到的字符不是数字时,就取栈顶的两个数来进行操作,并将操作完成的数再放入栈
class Solution {
public:
bool isNumber(const string& s)
{
return !(s == "+" || s == "-" || s == "*" || s == "/");
}
int evalRPN(vector<string>& tokens) {
stack<int> st;
for(const auto& e : tokens)
{
if(isNumber(e)) st.push(stoi(e));
else
{
int y = st.top();// 注意:这里是用x / y
st.pop();
int x = st.top();
st.pop();
switch(e[0])// 这里不能是e
{
case '+':
st.push(x + y);
break;
case '-':
st.push(x - y);
break;
case '*':
st.push(x * y);
break;
case '/':
st.push(x / y);
break;
}
}
}
return st.top();
}
};
现在可以使用一个map,第一个参数存放操作符,第二个参数存放对应的操作
class Solution {
public:
bool isNumber(const string& s)
{
return !(s == "+" || s == "-" || s == "*" || s == "/");
}
int evalRPN(vector<string>& tokens) {
stack<int> st;
map<string, function<int(int, int)>> opFuncMap = {
{"+", [](int x, int y){return x + y;}},
{"-", [](int x, int y){return x - y;}},
{"*", [](int x, int y){return x * y;}},
{"/", [](int x, int y){return x / y;}}
};
for(const auto& e : tokens)
{
if(isNumber(e)) st.push(stoi(e));
else
{
int y = st.top();// 注意:这里是用x / y
st.pop();
int x = st.top();
st.pop();
int ret = opFuncMap[e](x, y);
st.push(ret);
}
}
return st.top();
}
};
2、function包装器包装成员函数指针
2.1 静态成员函数
class Plus
{
public:
static int plusi(int a, int b)
{
return a + b;
}
double plusd(double a, double b)
{
return a + b;
}
};
int main()
{
// 包装静态成员函数
function<int(int, int)> f1 = Plus::plusi; // 要指定类域
cout << f1(1, 1) << endl;
return 0;
}
静态成员函数与普通函数是类似的,只是需要指定类域
2.2 非静态成员函数
int main()
{
// 包装静态成员函数
function<int(int, int)> f1 = Plus::plusi; // 要指定类域
cout << f1(1, 1) << endl;
// 包装非静态成员函数
function<double(double, double)> f2 = Plus::plusd;
return 0;
}
此时是错误的,非静态成员函数要取函数指针需要加&,静态成员函数可加可不加,最好也加上
int main()
{
// 包装静态成员函数
function<int(int, int)> f1 = &Plus::plusi; // 要指定类域
cout << f1(1, 1) << endl;
// 包装非静态成员函数
function<double(double, double)> f2 = &Plus::plusd;
return 0;
}
此时还是不行的,因为非静态成员函数还要隐藏的this指针,可以传指针,也可传对象(有名对象或匿名对象)
int main()
{
// 包装静态成员函数
function<int(int, int)> f1 = &Plus::plusi; // 要指定类域
cout << f1(1, 1) << endl;
// 包装非静态成员函数
Plus ps;
function<double(Plus*, double, double)> f2 = &Plus::plusd;
cout << f2(&ps, 7.7, 7.7) << endl;
// 包装非静态成员函数
function<double(Plus, double, double)> f3 = &Plus::plusd;
cout << f3(Plus(), 7.7, 7.7) << endl;
return 0;
}
此时就会有一个疑问,非静态成员函数多出来的参数是this指针,是一个指针,为什么可以传对象呢?
实际上,function本质并不是将3个参数传给plusd,是拿到函数指针后,将函数指针作为成员变量存起来,我们传参时实际上是调用了function的operator(),operator()又去调用plusd,而调用plusd就需要用到Plus的对象或指针。也就是说,不是直接将3个参数传给plusd,并且this指针也不支持显示传参
3、bind包装器
bind是一个函数模板,主要用于对可调用对象调整参数的顺序、个数,返回一个仿函数,直接使用auto接收
placeholders是一个命名空间,里面有很多标识符(_1,_2,_3,...),可用这些标识符去代表对应的参数,_1始终代表第一个实参,_2代表第二个实参
3.1 调整参数顺序
using placeholders::_1;
using placeholders::_2;
using placeholders::_3;
int Sub(int x, int y)
{
return (x - y) * 10;
}
int main()
{
// 调整参数顺序
auto sub1 = bind(Sub, _1, _2);
cout << sub1(10, 5) << endl;
auto sub2 = bind(Sub, _2, _1);
cout << sub2(10, 5) << endl;
return 0;
}
结果是50 -50。_1代表的始终是第1个实参,无论是sub1(10, 5),还是sub2(10, 5),第1个实参都是10,都会匹配到_1,但是sub1和sub2中_1处在的位置是不同的,sub1会将_1当成第1个形参来传,sub2会将_1当成是第2个形参来传,也就是说在调用函数时,是按顺序传的
3.2 调整参数个数
int main()
{
// 调整参数顺序
auto sub1 = bind(Sub, _1, _2);
cout << sub1(10, 5) << endl;
auto sub2 = bind(Sub, _2, _1);
cout << sub2(10, 5) << endl;
// 调整参数个数
auto sub3 = bind(Sub, 100, _1);
cout << sub3(5) << endl;
auto sub4 = bind(Sub, _1, 100);
cout << sub4(5) << endl;
return 0;
}
结果是950 -950
实际上,bind返回的是一个仿函数对象,也就是说上面的sub1、sub2、sub3、sub4都是仿函数对象,本质也是调用operator(),只是里面调用的顺序不同
其实,bind主要用于绑定一些固定参数,向上面非静态成员函数的传参每次都需要传指针或对象,可用直接绑定了,这样只需要传操作数
class Plus
{
public:
static int plusi(int a, int b)
{
return a + b;
}
double plusd(double a, double b)
{
return a + b;
}
};
int main()
{
function<double(double, double)> f1 = bind(&Plus::plusd, Plus(), _1, _2);
cout << f1(7.7, 7.7) << endl;
return 0;
}