C++学习记录——이십팔 C++11(4)

文章目录

  • 包装器
    • 1、functional
    • 2、绑定


这一篇比较简短,只是因为后要写异常和智能指针,所以就把它单独放在了一篇博客,后面新开几篇博客来写异常和智能指针

包装器

1、functional

包装器是一个类模板,对可调用对象类型进行再封装适配,可调用对象,比如函数指针,lambda等。包装器的头文件是functional。

template <class T> function;
template <class Ret, class... Args>
class function<Ret(Args...)>
模板参数说明:
Ret:被调用函数的返回类型
Args...:被调用函数的形参

实际使用

int f(int a, int b)
{
	cout << "f" << endl;
	return a + b;
}

struct Functor
{
public:
	int operator() (int a, int b)
	{
		cout << "Functor" << endl;
		return a + b;
	}
};

int main()
{
	//int(*pf1)(int, int) = f;函数指针
	function<int(int, int)> f1 = f;
	function<int(int, int)> f2 = Functor();
	function<int(int, int)> f3 = [](int a, int b) {
		cout << "lambda" << endl;
		return a + b; 
	};
	cout << f1(1, 2) << endl;
	cout << f2(10, 20) << endl;
	cout << f3(100, 200) << endl;
	return 0;
}

三个int,第一个是函数返回值类型,后两个是参数类型。包装起包装起来的就可以传给模板参数

	map<string, function<int(int, int)>> opFuncMap;
	opFuncMap["函数指针"] = f1;
	opFuncMap["仿函数"] = Functor();
	opFuncMap["lambda"] = [](int a, int b) {
		cout << "lambda" << endl;
		return a + b;
	};
	cout << opFuncMap["lambda"](1, 2) << endl;

看一个题

逆波兰表达式求值

给你一个字符串数组 tokens ,表示一个根据 逆波兰表示法 表示的算术表达式。

请你计算该表达式。返回一个表示表达式值的整数。

注意:

有效的算符为 ‘+’、‘-’、‘*’ 和 ‘/’ 。
每个操作数(运算对象)都可以是一个整数或者另一个表达式。
两个整数之间的除法总是 向零截断 。
表达式中不含除零运算。
输入是一个根据逆波兰表示法表示的算术表达式。
答案及所有中间计算结果可以用32位整数表示。

C++学习记录——이십팔 C++11(4)_第1张图片

C++学习记录——이십팔 C++11(4)_第2张图片

之前的写法

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

用包装器后

class Solution {
public:
    int evalRPN(vector<string>& tokens) {
        stack<int> st;
        map<string, function<int(int, int)>> opFuncMap = 
        {
            {"+", [](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; }}
        };//这里就是map的初始化,用C++11的列表初始化
        for(auto str : tokens)
        {
            if(opFuncMap.count(str))
            {
                int right = st.top();
                st.pop();
                int left = st.top();
                st.pop();
                st.push(opFuncMap[str](left, right));
            }
            else
            {
                st.push(stoi(str));
            }
        }
        return st.top();
    }
};

包装器也可以包装成员函数。

class Plus
{
public:
	static int plus1(int a, int b)
	{
		return a + b;
	}
	double plus2(double a, double b)
	{
		return (a + b) * _rate;
	}
private:
	int _rate = 2;
};

int main()
{
class Plus
{
public:
	Plus(int rate = 2)
		:_rate(rate)
	{}
	static int plus1(int a, int b)
	{
		return a + b;
	}
	double plus2(double a, double b)
	{
		return (a + b) * _rate;
	}
private:
	int _rate = 2;
};

int main()
{
	function<int(int, int)> f1 = Plus::plus1;
	function<int(Plus, double, double)> f2 = &Plus::plus2;
	cout << f1(1, 2) << endl;
	cout << f2(Plus(), 20, 20) << endl;
	Plus p1(3);
	cout << f2(p1, 20, 20) << endl;
    return 0;
}

静态成员函数可以直接调用,而非静态的需要在第一个位置加上类名,因为有this指针,然后后面的Plus前加上&,静态函数也可以加上这个&,使用这个函数的时候,非静态需要在第一个参数位置放上类的对象,可以是匿名对象,如果在声明f2时,传的是*Plus,那么下面调用时就必须传对象的地址,所以就不能传匿名对象的地址,因为右值无法取地址。

包装器本质上是仿函数,f1,f2,f3就是对象,然后调用operator(),传过去参数,然后operator()再去调用对应函数,传类的对象就用对象来调用,传指针就指针来调用。

2、绑定

绑定是一个函数模板,用来调整参数。绑定是一个通用的函数适配器,接受一个可调用对象,可调用对象就是三个,函数指针、lambda、仿函数,生成一个新的可调用对象来适配。

bind函数第一个参数是一个万能引用,左右值都可传,然后后面的是占位符,_1表示第一个参数,_2表示第二个参数,以此类推,这些占位符是一个placeholders空间里。

int Print(int a, int b)
{
	cout << a << " ";
	cout << b << endl;
}

int main()
{
	Print(10, 20);
	auto RP = bind(Print, placeholders::_2, placeholders::_1);
	RP(10, 20);//再次调用就换了顺序了。
	return 0;
}

如果bind写着_1在_2前面,那就没换顺序,要换顺序占位符就得对应着写。bind函数会返回一个对象,我们可以用auto来推演类型,还可以用function。实际调用的还是Print,不过适配器就是套了一个壳。

绑定真正有用的是改变参数个数

用这段代码做例子

class Sub
{
public:
	Sub(int rate)
		:_rate(rate)
	{}

	int func(int a, int b)
	{
		return (a - b) * _rate;
	}
private:
	int _rate;
};

class Solution {
public:
	int evalRPN(vector<string>& tokens) {
		stack<int> st;
		map<string, function<int(int, int)>> opFuncMap =
		{
			{"+", [](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; }}
		};//这里就是map的初始化,用C++11的列表初始化
		for (auto str : tokens)
		{
			if (opFuncMap.count(str))
			{
				int right = st.top();
				st.pop();
				int left = st.top();
				st.pop();
				st.push(opFuncMap[str](left, right));
			}
			else
			{
				st.push(stoi(str));
			}
		}
		return st.top();
	}
};

int main()
{
	function<int(Sub, int, int)> fSub = &Sub::func;
	fSub(Sub(1), 10, 20);
	return 0;
}

这是上面包装器的写法。这样的写法无法给opFuncMap传fSub对象,因为参数个数不一致,这时候就是绑定的作用体现了。

	function<int(int, int)> fSub = bind(&Sub::func, Sub(1), placeholders::_1, placeholders::_2);
	fSub(10, 20);

把Sub(1)对象显式地传给func函数,顺序没有变,只是第一个参数显示传,剩下两个就从_1开始排顺序。也可以对其他参数来绑定。

	function<int(int, int)> fSub = bind(&Sub::func, placeholders::_1, 10, placeholders::_2);
	fSub(Sub(1), 20);

本篇gitee

结束。

你可能感兴趣的:(C++学习,c++,学习)