【C++】C++11-包装器

目录

1、function包装器

2、function包装器包装成员函数指针

[2.1 静态成员函数](#2.1 静态成员函数)

[2.2 非静态成员函数](#2.2 非静态成员函数)

3、bind包装器

[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在不同时间生成的类型都不一样),仿函数只能写死,函数指针可以,但太麻烦,所以就可以考虑使用包装器来进行统一

我们通过一个题目来看

150. 逆波兰表达式求值 - 力扣(LeetCode)

这道题普通的做法是开一个栈,遍历数组,当遍历到的字符是数字时,就放入栈,当遍历到的字符不是数字时,就取栈顶的两个数来进行操作,并将操作完成的数再放入栈

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;
}
相关推荐
乌啼霜满天2497 分钟前
JDBC编程---Java
java·开发语言·sql
Smile丶凉轩8 分钟前
微服务即时通讯系统的实现(服务端)----(1)
c++·git·微服务·github
色空大师20 分钟前
23种设计模式
java·开发语言·设计模式
萝卜兽编程31 分钟前
优先级队列
c++·算法
Bruce小鬼33 分钟前
QT文件基本操作
开发语言·qt
2202_7544215438 分钟前
生成MPSOC以及ZYNQ的启动文件BOOT.BIN的小软件
java·linux·开发语言
我只会发热1 小时前
Java SE 与 Java EE:基础与进阶的探索之旅
java·开发语言·java-ee
懷淰メ1 小时前
PyQt飞机大战游戏(附下载地址)
开发语言·python·qt·游戏·pyqt·游戏开发·pyqt5
hummhumm1 小时前
第 22 章 - Go语言 测试与基准测试
java·大数据·开发语言·前端·python·golang·log4j
宁静@星空1 小时前
006-自定义枚举注解
java·开发语言