👀樊梓慕:个人主页
🎥个人专栏:《C语言》《数据结构》《蓝桥杯试题》《LeetCode刷题笔记》《实训项目》《C++》《Linux》《算法》
🌝每一个不曾起舞的日子,都是对生命的辜负
目录
我们目前学习过的可调用对象有三种:函数指针、仿函数以及lambda表达式(实际上也是仿函数),但是这三种可调用对象却又有各自的缺点,比如函数指针类型写起来比较复杂,仿函数的类型不统一,而lambda表达式语法层上就没有类型,所以C++11引入了包装器,主要就是为了封装他们,统一类型。
欢迎大家📂收藏📂以便未来做题时可以快速找到思路,巧妙的方法可以事半功倍。
=========================================================================
GITEE相关代码:🌟樊飞 (fanfei_c) - Gitee.com🌟
=========================================================================
包装器就是对可调用对象的再封装,C++中的function本质上就是一个 『 类模板』。
function类模板的原型如下:
- template <class T> function;
- template <class Ret, class... Args>
- class function<Ret(Args...)>;
其中Ret是被包装的可调用对象的返回类型,而Args则是被包装的可调用对象的形参类型。
function引入的目的就是为了统一三种可调用对象的类型。
那么我们如何进行包装呢?
- //普通函数
- int f(int a, int b)
- {
- return a + b;
- }
-
- //仿函数对象
- struct Functor
- {
- public:
- //仿函数
- int operator()(int a, int b)
- {
- return a + b;
- }
- };
- class Plus
- {
- public:
- //类的静态成员函数
- static int plusi(int a, int b)
- {
- return a + b;
- }
- //类的非静态成员函数
- double plusd(double a, double b)
- {
- return a + b;
- }
- };
- int main()
- {
- //1、包装函数指针(函数名)
- function<int(int, int)> func1 = f;
- cout << func1(1, 2) << endl;
-
- //2、包装仿函数(函数对象)
- function<int(int, int)> func2 = Functor();
- cout << func2(1, 2) << endl;
-
- //3、包装lambda表达式
- function<int(int, int)> func3 = [](int a, int b){return a + b; };
- cout << func3(1, 2) << endl;
-
- //4、类的静态成员函数
- //function
func4 = Plus::plusi; - function<int(int, int)> func4 = &Plus::plusi; //&可省略
- cout << func4(1, 2) << endl;
-
- //5、类的非静态成员函数
- function<double(Plus, double, double)> func5 = &Plus::plusd; //&不可省略
- cout << func5(Plus(), 1.1, 2.2) << endl;
- return 0;
- }
注意:
另外,因为类的非静态成员函数实际上还有一个隐藏的参数*this,所以再最后书写参数类型时,要将类的类型写在前面,并且传入匿名对象即可。
给你一个字符串数组 tokens
,表示一个根据 逆波兰表示法 表示的算术表达式。
请你计算该表达式。返回一个表示表达式值的整数。
注意:
'+'
、'-'
、'*'
和 '/'
。解题思路:
当时,我们是这样写的:
- class Solution {
- public:
- int evalRPN(vector
& tokens) { - stack<int> s;
- int sum=0;
- for(int i=0;i
size();i++) - {
- if(!(tokens[i]=="+" || tokens[i]=="-" || tokens[i]=="*" || tokens[i]=="/"))
- {
- s.push(atoi(tokens[i].c_str()));
- }
- else
- {
- int right=s.top();
- s.pop();
- int left=s.top();
- s.pop();
- switch(tokens[i][0])
- {
- case '+':
- s.push(left+right);
- break;
- case '-':
- s.push(left-right);
- break;
- case '*':
- s.push(left*right);
- break;
- case '/':
- s.push(left/right);
- break;
- }
- }
- }
- return s.top();
- }
- };
那现在我们学习了很多C++的新容器新内容,我们可以写出一份更加漂亮的代码出来。
我们可以利用map容器和function包装器对以上代码做优化。
map容器存储的是键值对,那么我们就可以将操作符字符串作为键,将具体要执行的操作作为值,那这样我们利用map的[]操作符就可以得到当前要进行什么操作了。
我们可以采用很多种方式将可调用对象赋给map的值,这里我们使用lambda表达式,你当然也可以使用函数指针、仿函数等,但明显这里使用lambda表达式更加方便。
但是lambda表达式没有类型,所以我们就可以使用包装器将lambda表达式包装成function包装器,统一了类型。
- class Solution {
- public:
- int evalRPN(vector
& tokens) { - stack<int> st;
- unordered_map
int(int, int)>> opMap = { - { "+", [](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; } }
- };
- for (const auto& str : tokens)
- {
- int left, right;
- if (str == "+" || str == "-" || str == "*" || str == "/")
- {
- right = st.top();
- st.pop();
- left = st.top();
- st.pop();
- st.push(opMap[str](left, right));
- }
- else
- {
- st.push(stoi(str));
- }
- }
- return st.top();
- }
- };
从这里,我们就可以看出来包装器function的意义:
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);
其中:
那我们上面说的调整参数的顺序就可以通过修改这个参数列表中的数据来实现了。
比如:
- int Sub(int a, int b)
- {
- return a - b;
- }
- int main()
- {
- //调整参数顺序
- auto f1 = bind(Sub, placeholders::_2, placeholders::_1);
- cout << f1(x, y) << endl;
-
- return 0;
- }
因为『 placeholders::_1』代表的就是第一个参数的位置,所以他与『 placeholders::_2』位置进行交换,就意味着封装后的f1对象,参数位置发生了改变。
那调整参数个数呢?
比如,由于非静态成员函数参数有一个隐藏的this指针,那么我们不想每次都要把它传进去,我们就可以利用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)> fc4 = bind(
- &Plus::plusd
- ,Plus()
- ,placeholders::_1
- ,placeholders::_2
- );
- cout << fc4(2, 3) << endl;
- //这样我们使用fc4时就传递两个参数就可以了,就不需要再每次将隐藏的this也传递了
-
- function<double(double)> fc5 = bind(
- &Plus::plusd
- ,Plus()
- ,placeholders::_1
- ,20
- );
- cout << fc5(2) << endl;
- //fc5绑死了两个参数
- return 0;
- }
以上这种绑死某个参数的做法可以用于比如这个参数很固定是某个值,我们就可以采用这种方法。
那么bind包装器的意义我们总结如下:
=========================================================================
如果你对该系列文章有兴趣的话,欢迎持续关注博主动态,博主会持续输出优质内容
🍎博主很需要大家的支持,你的支持是我创作的不竭动力🍎
🌟~ 点赞收藏+关注 ~🌟
=========================================================================