C++11之包装器

包装器

1.1 function包装器

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

cpp 复制代码
template<class 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;
	}
};

void test1()
{
	// 函数指针
	std::cout << useF(f, 11.11) << std::endl;

	// 函数对象(仿函数)
	std::cout << useF(Functor(), 11.11) << std::endl;

	// lamber表达式
	std::cout << useF([](double d)->double { return d / 4; }, 11.11) << std::endl;
}

通过上面的程序验证,我们会发现useF函数模板实例化了三份。包装器可以很好的解决上面的问题

function在头文件<functional>

类模板原型如下

cpp 复制代码
template <class T> function;     // undefined
template <class Ret, class... Args>
class function<Ret(Args...)>;

模板参数说明:

Ret : 被调用函数的返回类型

Args...:被调用函数的形参

1.2包装器的使用

有了包装器,我们就可以把上面的三份不同的对象存储到一个vector中

cpp 复制代码
void test2()
{
	//包装器
	function<double(double)> f1 = f;//函数指针
	function<double(double)> f2 = Functor();//仿函数对象
	function<double(double)> f3 = [](double d)->double { return d / 4; };//lambda

	vector<function<double(double)>> v = { f1,f2,f3 };

	double n = 3.14;
	for (auto f : v)
	{
		cout << f(n++) << endl;
	}
}

使用方法如下:

cpp 复制代码
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;
	}
};
void test4()
{
	// 函数名(函数指针)
	std::function<int(int, int)> func1 = f;
	cout << func1(1, 2) << endl;

	// 函数对象
	std::function<int(int, int)> func2 = Functor();
	cout << func2(1, 2) << endl;

	// lamber表达式
	std::function<int(int, int)> func3 = [](const int a, const int b)
		{return a + b; };
	cout << func3(1, 2) << endl;

	// 类的成员函数
	std::function<int(int, int)> func4 = &Plus::plusi;
	cout << func4(1, 2) << endl;

	std::function<double(Plus, double, double)> func5 = &Plus::plusd;
	cout << func5(Plus(), 1.1, 2.2) << endl;
}

1.3包装器的应用

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

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

cpp 复制代码
class Solution {
public:
	int evalRPN(vector<string>& tokens) {
		stack<int> st;
		map<string, function<int(int, int)>> opFuncMap =
		{
		{ "+", [](int i, int j) {return i + j; } },
		{ "-", [](int i, int j) {return i - j; } },
		{ "*", [](int i, int j) {return i * j; } },
		{ "/", [](int i, int j) {return i / j; } },
		{ "&", [](int i, int j) {return i & j; } }
		};
		for (auto& str : tokens)
		{
			if (opFuncMap.find(str) != opFuncMap.end())
			{
				//操作符
				int right = st.top();
				st.pop();

				int left = st.top();
				st.pop();

				//通过[]去调用包装器的lambda
				st.push(opFuncMap[str](left, right));
			}
			else
			{
				//操作数
				// 1、atoi itoa
				// 2、sprintf scanf
				// 3、stoi to_string C++11
				st.push(stoi(str));
			}
		}
		return st.top();
	}
};

1.4 bind包装器

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

/原型如下:

cpp 复制代码
template <class Fn, class... Args>
/* unspecified */ bind(Fn&& fn, Args&&... args);
// with return type (2) 
template <class Ret, class Fn, class... Args>
/* unspecified */ bind(Fn&& fn, Args&&... args);

可以将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为第二个参数,以此类推。

1.5 bind包装器的使用

例1:

cpp 复制代码
int sub(int x, int y)
{
	return x - y;
}

void test5()
{
	//保持x-y不变
	function<int(int, int)> resub1 = bind(sub, placeholders::_1, placeholders::_2);
	cout << resub1(10, 5) << endl;

	//交换x,y相减
	function<int(int, int)> resub2 = bind(sub, placeholders::_2, placeholders::_1);
	cout << resub2(10, 5) << endl;
}

例2:

cpp 复制代码
int Plus(int a, int b)
{
	return a + b;
}
class Sub
{
public:
	int sub(int a, int b)
	{
		return a - b;
	}

	static int plus(int a, int b)
	{
		return a + b;
	}
};
void test6()
{
	//表示绑定函数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);
	
	//对于非静态的类域中的函数要加上一个定义的对象
	function<int(int, int)> func4 = bind(&Sub::sub, s,placeholders::_2, placeholders::_1);
	//传匿名对象最方便
	function<int(int, int)> func5 = bind(&Sub::sub, Sub(), placeholders::_2, placeholders::_1);
	cout << func3(1, 2) << endl;
	cout << func4(1, 2) << endl;

	//对于静态的类域的函数
	function<int(int, int)> func6 = bind(&Sub::plus, placeholders::_2, placeholders::_1);
}

注意:bind在传类的成员函数的时候,如果该函数是静态的,就正常传,如果该函数不是静态的,就需要传类的地址,并且还需要传一个类的对象或者对象的指针。

总结:也就是当我们需要对一些函数接口进行传参顺序的改变,就可以使用bind包装器。

相关推荐
wabs66610 小时前
关于贪心算法的一些自我总结【力扣45.跳跃游戏II】【灵感来源:代码随想录】
算法·贪心算法·复盘
2401_8769641310 小时前
【湖北专升本】2026湖北专升本真题PDF+备考资料汇总
数据结构·人工智能·经验分享·深度学习·算法·计算机视觉
basketball61610 小时前
C++ NULL 和 nullptr 区别 以及 nullptr 的核心实现
java·开发语言·c++
旺仔来了11 小时前
不联网的Linux下部署python环境
linux·开发语言·python
嗝o゚11 小时前
CANN GE 算子融合——融合算法与调度策略
算法·昇腾·cann·ge
小江的记录本11 小时前
【JVM虚拟机】垃圾回收GC:垃圾回收算法:标记-清除、标记-复制、标记-整理、分代收集(附《思维导图》+《面试高频考点清单》)
java·jvm·后端·python·算法·安全·面试
烛衔溟11 小时前
TypeScript 类的类型 —— 作为类型使用
javascript·ubuntu·typescript
之歆11 小时前
Day16_JavaScript 轮播图与事件工程实战(下篇)
服务器·开发语言·前端·javascript·网络·性能优化
Fre丸子_12 小时前
自定义文件夹选取功能
c++
会Tk矩阵群控的小木12 小时前
云控系统在TikTok多账号管理中的核心应用与技术实现
开发语言·php·开源软件·个人开发·tk矩阵