南京建设监理协会网站,网站搜索推广销售,上海企业网站制作哪家好,免费黄页网站#x1f308;欢迎来到C专栏~~包装器解析 (꒪ꇴ꒪(꒪ꇴ꒪ )#x1f423;,我是Scort目前状态#xff1a;大三非科班啃C中#x1f30d;博客主页#xff1a;张小姐的猫~江湖背景快上车#x1f698;#xff0c;握好方向盘跟我有一起打天下嘞#xff01;送给自己的一句鸡汤欢迎来到C专栏~~包装器解析 (꒪ꇴ꒪(꒪ꇴ꒪ ),我是Scort目前状态大三非科班啃C中博客主页张小姐的猫~江湖背景快上车握好方向盘跟我有一起打天下嘞送给自己的一句鸡汤真正的大师永远怀着一颗学徒的心作者水平很有限如果发现错误可在评论区指正感谢欢迎持续关注 文章目录欢迎来到C专栏~~包装器解析 概念类型统一✨例题求解逆波兰表达式✨包装器的意义bind 包装器bind 绑定固定参数调整传参顺序bind包装器的意义写在最后概念
function包装器 也叫作适配器。C中的function本质是一个类模板也是一个包装器。 那么我们来看看我们为什么需要function呢
包装器定义式
// 类模板原型如下
template class T function; // undefined
template class Ret, class... Args
class functionRet(Args...);模板参数说明
Ret 是被包装的可调用对象的返回值类型Args... 是被包装的可调用对象的形参类型
function包装器可以对可调用对象进行包装包括函数指针、函数名、仿函数函数对象、lambda表达式
int f(int a, int b)
{return a b;
}struct Functor
{
public:int operator() (int a, int b){return a b;}
};class Plus
{
public://静态 vs 非静态static int plusi(int a, int b){return a b;}double plusd(double a, double b){return a b;}
};int main()
{functionint(int, int) f1 f;f1(1, 2);functionint(int, int) f2 Functor();f2(1, 2);functionint(int, int) f3 Plus::plusi;f3(1, 2);//非静态成员函数 要 对象functiondouble(Plus, double, double) f4 Plus::plusd;f4(Plus(), 1.1, 2.2);return 0;
}注意事项
取静态成员函数的地址可以不用取地址运算符“”但取非静态成员函数的地址必须使用取地址运算符“”非静态成员函数在调用的时候要 对象因为非静态成员函数的第一个参数是隐藏this指针所以在包装时需要指明第一个形参的类型为类的类型。
类型统一
对于以下函数模板useF
传入该函数模板的第一个参数可以是任意的可调用对象比如函数指针、仿函数、lambda表达式等useF中定义了静态变量count并在每次调用时将count的值和地址进行了打印可判断多次调用时调用的是否是同一个useF函数。
代码如下
templateclass F, class T
T useF(F f, T x)
{static int count 0;cout count: count endl;cout count: count endl;return f(x);
}在不使用包装器直接传入对象的时候会实例化出三份
double f(double i)
{return i / 2;
}
struct Functor
{double operator()(double d){return d / 3;}
};
int main()
{//函数指针cout useF(f, 11.11) endl;//仿函数cout useF(Functor(), 11.11) endl;//lambda表达式cout useF([](double d)-double{return d / 4; }, 11.11) endl;return 0;
}输出结果如下
由于函数指针、仿函数、lambda表达式是不同的类型因此useF函数会被实例化出三份三次调用useF函数所打印count的地址也是不同的。
但实际这里根本没有必要实例化出三份useF函数因为三次调用useF函数时传入的可调用对象虽然是不同类型的但这三个可调用对象的返回值和形参类型都是相同的这时就可以用包装器分别对着三个可调用对象进行包装然后再用这三个包装后的可调用对象来调用useF函数这时就只会实例化出一份useF函数根本原因就是因为包装后这三个可调用对象都是相同的function类型因此最终只会实例化出一份useF函数该函数的第一个模板参数的类型就是function类型的
包装后代码如下
int main()
{// 函数指针functiondouble(double) f1 f;cout useF(f1, 11.11) endl;// 函数对象functiondouble(double) f2 Functor();cout useF(f2, 11.11) endl;// lamber表达式functiondouble(double) f3 [](double d)-double { return d / 4; };cout useF(f3, 11.11) endl;return 0;
}✨例题求解逆波兰表达式
题目地址传送
解题思路
首先定义一个栈依次遍历所给字符串遇到数字直接入栈遇到操作符则从栈定抛出两个数字进行对应的运算并将运算后得到的结果压入栈中所给字符串遍历完毕后栈顶的数字就是逆波兰表达式的计算结果
此处的包装器 建立各个运算符与其对应需要执行的函数之间的映射关系当需要执行某一运算时就可以直接通过运算符找到对应的函数进行执行 当运算类型增加时就只需要建立新增运算符与其对应函数之间的映射关系即可其他代码不用动
class Solution {
public:int evalRPN(vectorstring tokens) {stacklong long st;mapstring, functionlong long(long long, long long) opfuncMap {//自动构造pair ~ 初始化列表构造{, [](long long a , long long b){return a b;}},{-, [](long long a , long long b){return a - b;}},{*, [](long long a , long long b){return a * b;}},{/, [](long long a , long long b){return a / b;}},};for(auto str : tokens){if(opfuncMap.count(str)){//操作符 出栈(先出右再出左)long long right st.top();st.pop();long long left st.top();st.pop();st.push(opfuncMap[str](left, right));}else{//操作数入栈st.push(stoll(str));}}return st.top();}
};✨包装器的意义
将可调用对象的类型进行统一便于我们对其进行统一化管理。包装后明确了可调用对象的返回值和形参类型更加方便使用者使用。
bind 包装器
bind 是一种函数包装器也叫做适配器。它可以接受一个可调用对象生成一个新的可调用对象来“适应”原对象的参数列表C 中的 bind 本质还是一个函数模板
bind函数模板的原型如下
template class Fn, class... Args
/* unspecified */ bind(Fn fn, Args... args);
template class Ret, class Fn, class... Args
/* unspecified */ bind(Fn fn, Args... args);模板参数说明
fn可调用对象args...要绑定的参数列表值或占位符
调用bind的一般形式
auto newCallable bind(callable, arg_list);callable需要包装的可调用对象 newCallable生成的新的可调用对象 arg_list逗号分隔的参数列表对应给定的 callable 的参数当调用 newCallable时newCallable 会调用 callable并传给它 arg_list 中的参数 _1 _2 ... 是定义在placeholders命名空间中代表绑定函数对象的形参_1代表第一个形参_2代表第二个形参 …
举例
using namespace placeholders;
int x 2, y 10;
Div(x, y);auto bindFun1 bind(Div, _1, _2);bind 绑定固定参数
原本传入的参数要求是要3个现在只需要输入两个参数因为绑定了固定的函数对象
using namespace placeholders;class Sub
{
public:int sub(int a, int b){return a - b;}
};int main()
{//functionint(Sub, int, int) fsub Sub::sub;functionint(int, int) fsub bind(Sub::sub, Sub(), _1, _2);
}想把Mul函数的第三个参数固定绑定为1.5可以在绑定时将参数列表的placeholders::_3设置为1.5。比如
int Mul(int a, int b, int rate)
{return a * b * rate;
}int main()
{functionint(int, int) fmul bind(Mul, _1, _2, 1.5);
}调整传参顺序
对于 Sub 类中的 sub 函数因为 sub 的第一个参数是隐藏的 this 指针如果想要在调用 sub 时不用对象进行调用那么可以将 sub 的第一个参数固定绑定为一个 Sub 对象
using namespace placeholders;class Sub
{
public:int sub(int a, int b){return a - b;}
};
int main()
{//绑定固定参数functionint(int, int) func bind(Sub::sub, Sub(), _1, _2);cout func(1, 2) endl; return 0;
}此时调用对象时就只需要传入用于相减的两个参数因为在调用时会固定帮我们传入一个匿名对象给 this 指针。
如果想要将 sub 的两个参数顺序交换那么直接在绑定时将 _1 和_2 的位置交换一下就行了
using namespace placeholders;class Sub
{
public:int sub(int a, int b){return a - b;}
};
int main()
{//绑定固定参数functionint(int, int) func bind(Sub::sub, Sub(), _2, _1);cout func(1, 2) endl; return 0;
}其原理第一个参数会传给_1第二个参数会传给 _2因此可以在绑定时通过控制 _n 的位置来控制第 n 个参数的传递位置
bind包装器的意义
将一个函数的某些参数绑定为固定的值让我们在调用时可以不用传递某些参数。可以对函数参数的顺序进行灵活调整。
写在最后
kd