【C++11】包装器

function包装器 也叫作适配器。C++中的function本质是一个类模板,也是一个包装器。

那我们为什么需要包装器?

假设我们有三个可调用对象,函数指针,仿函数,lambda,需要将这三个可调用对象分别与一个字符串对应起来。

说到对应我们肯定就想到了映射。
于是我们map<string, ??> dict
那我们模板的第二个参数填什么?这三个可调用对象的类型都不一样,尤其是lambda,他的类型更是一坨。

于是就出现了function。

function语法:

std::function在头文件<functional>

template <class Ret, class... Args>
class function<Ret(Args...)>;

模板参数说明:
Ret: 被调用函数的返回类型
Args…:被调用函数的形参

function示例:

function包装函数指针 && 仿函数 && lambda:

int FunPtr(int x, int y)
{
	cout << "int FunPtr(int x, int y)";
	return x + y;
}

class Functor
{
public:
	int operator()(int x, int y)
	{
		cout << "int operator()(int x, int y)";
		return x + y;
	}
};

int main()
{
	function<int(int, int)> f1 = FunPtr;
	cout << setw(15) << "by f1 : " << f1(1, 2) << endl;

	function<int(int, int)> f2 = Functor();
	cout << setw(15) << "by f2 : " << f2(1, 2) << endl;

	function<int(int, int)> f3 = [](int x, int y)->int { cout << "[](int x, int y)->int"; return x + y; };
	cout << setw(15) << "by f3 : " << f3(1, 2) << endl;

	return 0;
}

结果:
在这里插入图片描述
也确实是使用同一个类型进行包装并进行了期望的调用。

同样,我们也可以进行包装类成员函数。
这里又分为两类:static成员函数和非static成员函数。

我们先来看static成员函数,与上三者差别不大。

class Myclass
{
public:
	static int func1(int x, int y)
	{
		
		return x * y * Myclass()._rate;
	}
	int func2(int x, int y)
	{

		return x * y * Myclass()._rate;
	}
private:
	int _rate = 10;
};

----------------------------------------

function<int(int, int)> f4 = &Myclass::func1;
cout << f4(1, 2) << " By f4." << endl;

比普通的多了一个需要指定类域的操作,另外,这个&可加可不加,但是非静态一定要加。


在来看静态:

class Myclass
{
public:
	static int func1(int x, int y)
	{
		
		return x * y * Myclass()._rate;
	}
	int func2(int x, int y)
	{

		return x * y * Myclass()._rate;
	}
private:
	int _rate = 10;
};

----------------------------------------
类类型:
function<int(Myclass, int, int)> f5 = &Myclass::func2;
cout << f5(Myclass(), 1, 2) << " By f4." << endl;
类指针:
function<int(Myclass*, int, int)> f6 = &Myclass::func2;
Myclass myclass;
cout << f6(&myclass, 1, 2) << " By f4." << endl;

因为非静态成员有this指针!所以模板为3个类型也是无可厚非。
但是要注意function模板的第一个参数是类类型,也可以是类指针指针。

为什么呢?
因为我们的function底层也是仿函数,但是我们给模板的类型不是直接作为成员函数的类型,同理,我们调用时也不是直接将参数传给成员函数。

而是我们要通过传过去的对象进行调用成员函数,而且this指针我们也不是想给就能给的,这是编译器干的事情我们不能越俎代庖。

使用function优化逆波兰表达式代码:

题目链接
在这里插入图片描述
对于此题目我们简单说一下思路:
使用栈进行解决,是操作数的话push栈中,是操作符的话pop出栈中的前两个元素进行与操作符对应的算法再push入栈中。
最后栈中剩余的即使答案。

代码:

class Solution {
public:
    int evalRPN(vector<string>& tokens)
    {
        stack<int> st;
        for (auto& val : tokens)
        {
            if (val == "+" || val == "-" || val == "*" || val == "/")
            {
                int right = st.top();
                st.pop();
                int left = st.top();
                st.pop();
                switch (val[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(val));
            }
        }
        return st.top();
    }
};

注意到我们上图代码是对于每一种操作符都进行了不同的操作,虽然完成了任务,但是代码不够优雅,因此我们可以使用包装器来进行优化。


博主第一次见到也是惊为天人,甚至AI都毫不吝啬夸奖。在这里插入图片描述
我自己写的代码从来没从他这里得到好的评价[哭]



class Solution {
public:
    int evalRPN(vector<string>& tokens)
    {
        stack<int> st;
        map <string, function<void(int, int)>> dict =
        {
            {"+", [&](int left, int right) { st.push(left + right); }},
            {"-", [&](int left, int right) { st.push(left - right); }},
            {"*", [&](int left, int right) { st.push(left * right); }},
            {"/", [&](int left, int right) { st.push(left / right); }},
        };
        for (auto& val : tokens)
        {
            if (val == "+" || val == "-" || val == "*" || val == "/")
            {
                int right = st.top();
                st.pop();
                int left = st.top();
                st.pop();
                dict[val](left, right);
            }
            else
            {
                st.push(stoi(val));
            }
        }
        return st.top();
    }
};

bind语法:

我们主要还是了解为主,见到认识,用的时候会用即可。

std::bind函数定义在头文件中,是一个函数模板,它就像一个函数包装器(适配器),接受一个可调用对象(callable object),生成一个新的可调用对象来“适应”原对象的参数列表。一般而言,我们用它可以把一个原本接收N个参数的函数fn,通过绑定一些参数,返回一个接收M个(M可以大于N,但这么做没什么意义)参数的新函数。同时,使用std::bind函数还可以实现参数顺序调整等操作。

可以将bind函数看作是一个通用的函数适配器,它接受一个可调用对象,生成一个新的可调用对象来“适应”原对象的参数列表。

调用bind的一般形式:
auto newCallable = bind(callable,arg_list);
其中,newCallable本身是一个可调用对象,arg_list是一个逗号分隔的参数列表,对应给定的callable的参数。当我们调用newCallable时,newCallable会调用callable,并传给它arg_list中的参数。

arg_list中的参数可能包含形如_n的名字,其中n是一个整数,这些参数是“占位符”,表示newCallable的参数,它们占据了传递给newCallable的参数的“位置”。数值n表示生成的可调用对象中参数的位置:_1为newCallable的第一个参数,_2为第二个参数,以此类推。

bind示例:

#include <functional>
int Plus(int a, int b)
{
    return a + b;
}
class Sub
{
public:
    int sub(int a, int b)
    {
        return a - b;
    }
};
int main()
{
    //表示绑定函数plus 参数分别由调用 func1 的第一,二个参数指定
    std::function<int(int, int)> func1 = std::bind(Plus, placeholders::_1,
        placeholders::_2);
    //auto func1 = std::bind(Plus, placeholders::_1, placeholders::_2);
    //func2的类型为 function<void(int, int, int)> 与func1类型一样
    //表示绑定函数 plus 的第一,二为: 1, 2
    auto func2 = std::bind(Plus, 1, 2);
    cout << func1(1, 2) << endl;
    cout << func2() << endl;
    Sub s;
    // 绑定成员函数
    std::function<int(int, int)> func3 = std::bind(&Sub::sub, s,
        placeholders::_1, placeholders::_2);
    // 参数调换顺序
    std::function<int(int, int)> func4 = std::bind(&Sub::sub, s,
        placeholders::_2, placeholders::_1);
    cout << func3(1, 2) << endl;
    cout << func4(1, 2) << endl;
    return 0;
}

结束~

  • 9
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值