C++第四十五弹---深入理解包装器:提升代码复用性与安全性的利器

news2024/9/28 15:25:30

 ✨个人主页: 熬夜学编程的小林

💗系列专栏: 【C语言详解】 【数据结构详解】【C++详解】

目录

1 包装器

1.1、function包装器

1.2、bind


1 包装器


1.1、function包装器


function包装器 也叫作适配器。C++中的function本质是一个类模板,也是一个包装器。那么我们来看看,我们为什么需要function呢?

先看一句代码!!!

auto ret = func(x);

上面func可能是什么呢?那么func可能是函数名函数指针函数对象(仿函数对象)?也有可能是lambda表达式对象?所以这些都是可调用的类型!如此丰富的类型,可能会导致模板的效率低下!为什么呢?我们继续往下看。

代码演示

// 函数模板
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;
	}
};
int main()
{
	// 函数名
	cout << useF(f, 11.11) << endl;
	// 函数对象
	cout << useF(Functor(), 11.11) << endl;
	// lamber表达式
	cout << useF([](double d)->double { return d / 4; }, 11.11) << endl;
	return 0;
}

运行结果

通过上面的程序验证,我们会发现useF函数模板实例化了三份,因为count的三个地址都不一样。 

包装器可以很好的解决上面的问题!!!

function原型

std::function在头文件<functional>
// 类模板原型如下
template <class T> function;     // undefined
template <class Ret, class... Args>
class function<Ret(Args...)>;
模板参数说明:
Ret: 被调用函数的返回类型
Args…:被调用函数的形参

代码演示

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;
	}
};

int main()
{
	// 普通函数
	std::function<double(double)> func1 = f;
	cout << useF(func1, 11.11) << endl;
	// 函数对象
	std::function<double(double)> func2 = Functor();
	cout << useF(func2, 11.11) << endl;
	// lamber表达式
	std::function<double(double)> func3 = [](double d)->double { return d /
		4; };
	cout << useF(func3, 11.11) << endl;

	return 0;
}

运行结果 

通过上面的程序验证,我们会发现useF函数模板只实例化了一份,因为count的三个地址都相同。  

包装成员函数指针

代码演示

class Plus
{
public:
	// 静态成员函数,没有this指针
	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, 2) << endl;

	// 有三个参数
	function<double(Plus*,double, double)> f2 = &Plus::plusd;
	Plus plus;
	cout << f2(&plus, 1.1, 2.2) << endl;

	// 不能显示传this指针,因此语法层面只要类型匹配即可
	function<double(Plus, double, double)> f3 = &Plus::plusd;
	cout << f3(Plus(), 1.1, 2.2) << endl;

	return 0;
}

运行结果

 

1.2、bind


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

bind原型

// 原型如下:
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为第二个参数,以此类推。

代码演示一

int Sub(int a,int b)
{
	return a - b;
}

int main()
{
	auto f1 = Sub;
	cout << f1(10, 5) << endl;

	// 调整两个参数顺序
	auto f2 = bind(Sub, placeholders::_2, placeholders::_1);
	cout << f2(10, 5) << endl;

	cout << typeid(f1).name() << endl;
	cout << typeid(f2).name() << endl;

	return 0;
}

运行结果 

代码分析 

代码演示二

class Sub
{
public:
	int sub(int a, int b)
	{
		return a - b;
	}
};

int main()
{
	// 调整参数个数,原本三个参数,加上this指针,第一个参数用匿名对象
	auto f4 = bind(&Sub::sub, Sub(), placeholders::_1, placeholders::_2);
	cout << f4(10, 5) << endl;

	Sub sub;
	// 第一个参数用对象地址
	auto f5 = bind(&Sub::sub, &sub, placeholders::_1, placeholders::_2);
	cout << f5(10, 5) << endl;
	return 0;
}

运行结果

代码演示三

void fx(const string& name, int x, int y)
{
	cout << name << "-> [血量:" << x << ",蓝:" << y << ']' << endl;
}
int main()
{
	fx("王昭君", 80, 30);
	fx("王昭君", 77, 20);
	fx("王昭君", 60, 0);
	fx("王昭君", 30, 40);

	fx("亚瑟", 90, 20);
	fx("亚瑟", 77, 15);
	fx("亚瑟", 40, 0);
	fx("亚瑟", 2, 20);

	return 0;
}

 运行结果

从代码三我们能够打印一个英雄的属性,但是调用同一个名字太冗余,可以绑定名字。

代码三优化

int main()
{
	// 绑定名字
	auto f6 = bind(fx, "王昭君", placeholders::_1, placeholders::_2);
	f6(80, 30);
	f6(77, 20);
	f6(60, 0);
	f6(30, 40);

	auto f7 = bind(fx, "亚瑟", placeholders::_1, placeholders::_2);
	f7(90, 20);
	f7(77, 15);
	f7(40, 0);
	f7(2, 20);
	return 0;
}

运行结果 

除了绑定第一个参数我们还可以绑定中间的参数,比如我们可以将血量绑定成80,代码如下: 

代码演示四

int main()
{
	// 绑定血量
	auto f8 = bind(fx, placeholders::_1, 80, placeholders::_2);
	f8("武则天", 12);
	f8("妲己", 33);
	return 0;
}

运行结果

代码演示四也可以使用 包装器 + 绑定。 

// 包装器 + 绑定
function<void(std::string,int)> f8 = bind(fx, placeholders::_1, 80, placeholders::_2);

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2104094.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

uniapp树洞烦恼分享系统 微信小程序设计与实现 80igt

目录 博主介绍技术栈系统设计&#x1f31f;文末获取源码数据库&#x1f31f;具体实现截图后端前端java类核心代码部分展示可行性论证个人心得系统测试操作可行性源码获取详细视频演示 博主介绍 &#x1f447;&#x1f3fb; 博主介绍&#xff1a;&#x1f447;&#x1f3fb; 专…

使用 WARP 和 Perf 测试对 MinIO 企业对象存储进行基准测试

AI/ML、高级分析和数据库等现代应用程序需要高性能对象存储。MinIO Enterprise Object Store 将可扩展性和高性能相结合&#xff0c;使每个工作负载&#xff08;无论要求多么苛刻&#xff09;触手可及。我们发布的基准测试表明&#xff0c;MinIO Enterprise Object Storage 是市…

泰克Tektronix MSO46 一款混合信号示波器

Tektronix MSO46 是一款混合信号示波器 (MSO)&#xff0c;专为调试和分析复杂的电子电路而设计。FlexChannel 技术使每个通道输入都可以用作单个模拟通道、八个数字逻辑输入&#xff08;使用 TLP058 逻辑探头&#xff09;或同时使用模拟和频谱视图&#xff0c;每个域都有独立的…

前端进阶|一文理解柯里化的逆操作,什么是反柯里化

温故而知新 在说反柯里化之前&#xff0c;先来复习下柯里化的基础。之前文章&#xff0c;我们了解了什么是柯里化&#xff0c;以及柯里化的实现原理&#xff0c;同时我们也明白了什么情况下我们使用柯里化&#xff0c;详细阅读参见之前文章《前端进阶&#xff5c;由浅入深的理…

探索Python世界的趣味之旅:自制贪吃蛇游戏

通过本次贪吃蛇游戏的开发实践&#xff0c;不仅可以掌握Python编程语言的基础知识&#xff0c;还深入了解了游戏开发的基本流程和技术要点。这只是一个开始&#xff0c;Python的世界远不止于此。未来&#xff0c;你可以尝试开发更复杂、更有趣的游戏项目&#xff0c;甚至探索人…

Java详解String 字符串类以及String内存原理、StringBuilder类、StringJoiner类(附有代码+案例)

文章目录 九.String 字符串类型9.0 String概述9.1 字符串常用方法9.2 String内存图9.2.1直接赋值9.2.2new出来 9.3字符串比较9.4 字符串遍历9.4.1 统计字符串大小写及数字9.4.2 拼接字符串9.4.3字符串反转 9.5 StringBuilder类9.5.1StringBuilder 构造方法9.5.2StringBuilder常…

Spring全局异常处理HandlerExceptionResolver使用

1 引言 全局异常处理在项目中经常会用到&#xff0c;主要作用包括统一处理异常、提供友好的错误信息、避免应用程序崩溃、记录异常日志、避免异常信息泄露等等。下文将以实现HandlerExceptionResolver接口的方式&#xff0c;实现全局异常处理功能及常规用法。 2 代码 下面列…

Qt 字符串的编码方式,以及反斜杠加3个数字是什么编码\344\275\240,如何生成

Qt 字符串的编码方式 问题 总所周知&#xff0c;Qt的ui文件在编译时&#xff0c;会自动生成一个ui_xxxxx.h的头文件&#xff0c;打开一看&#xff0c;其实就是将摆放的控件new出来以及布局的代码。 只要用Qt提供的uic.exe工具&#xff0c;自己也可以将ui文件输出为代码文件…

day17JS-Cookle、webStorage和Promise

1. 什么是Cookle 在网页中需要向当前电脑中存储一些数据&#xff0c;通过cookie可以存储。 Cookie &#xff1a;是一种由服务器发送到用户浏览器并保存在用户计算机上的小型文本文件&#xff0c;用于识别用户身份、跟踪用户活动、保存用户设置等。它通常包括名称、值、域名、…

云服务器+docker:还在为项目上线苦恼?一文包你解决(保姆级教程,图文并茂,万字起步!!!)

目录 Blue留言机&#xff1a; 学习视频以及参考资料 &#xff1a; 1、学习之前的必备操作&#xff1a; 第一步&#xff1a;购买服务器 选择一台免费的云服务器&#xff08;包白嫖的&#xff09; 配置服务器的一点说明&#xff1a; 查看自己是否购买成功&#xff1a; 第…

解决UOS开发截图时电脑滴滴声

在使用import命令截图时&#xff0c;电脑会伴随滴滴声&#xff0c;影响程序使用体验&#xff0c;使用 sudo rmmod pscpkr 即可解决滴滴声&#xff0c;这个命令每次开机&#xff0c;重新运行程序时都需输入&#xff0c;因此需要在脚本文件中自动执行&#xff0c;但是sudo命令需…

Tomcat8版本以上配置自定义400错误页面

问题&#xff1a;当请求URL包含特殊字符时&#xff0c;Tomcat会自动拦截并返回400状态码页面&#xff0c;暴露了tomcat的版本信息&#xff0c;这是很严重的安全漏洞。 java.lang.IllegalArgumentException: 在请求目标中找到无效字符。有效字符在RFC 7230和RFC 3986中定义 解…

Kafka【六】Linux下安装Kafka(Zookeeper)集群

Kafka从早期的消息传输系统转型为开源分布式事件流处理平台系统&#xff0c;所以很多核心组件&#xff0c;核心操作都是基于分布式多节点的。本文这里采用三台虚拟机模拟真实物理主机搭建Zookeeper集群和kafka集群。 VMware可以使用户在一台计算机上同时运行多个操作系统&…

符合全真彩的简单完美正方形

符合全真彩的简单完美正方形&#xff0c;像素&#xff1a;4096x4096、颜色&#xff1a;16,777,216。 原图可透过 https://linxinglu.com/art/#245.png 访问 &#xff0c;除了分辨率 4096x4096&#xff0c;也就是 16777216 像素&#xff0c;颜色数量也恰好是 16777216&#xff0…

静态字段和静态方法

1. 静态字段 1. 用static修饰的字段。 2. 实例字段在每个实例中有自己的独立”空间“&#xff0c;静态字段只有共享”空间“&#xff0c;所有实例共享该字段。 3. 使用”类名.静态字段“调用静态字段&#xff0c;静态字段不属于实例&#xff0c;是类class本身的…

四川财谷通抖音小店创新引领新风尚

在数字化浪潮的推动下&#xff0c;电商行业蓬勃发展&#xff0c;抖音小店作为新兴的电商平台&#xff0c;凭借其独特的社交属性和便捷的购物体验&#xff0c;迅速赢得了广大消费者的青睐。在众多抖音小店中&#xff0c;四川财谷通抖音小店以其精准定位、高质量内容、一站式服务…

iLogtail 开源两周年:社区使用调查报告

作者&#xff1a;玄飏 iLogtail 作为阿里云开源的可观测数据采集器&#xff0c;以其高效、灵活和可扩展的特性&#xff0c;在可观测采集、处理与分析领域受到了广泛的关注与应用。在 iLogtail 两周年之际&#xff0c;我们对 iLogtail 开源社区进行了一次使用调研&#xff0c;旨…

http连接处理(最新版)

分析http类及请求接收 基础 epoll epoll_create函数 #include <sys/epoll.h> int epoll_create(int size) 创建一个指示epoll内核事件表的文件描述符&#xff0c;该描述符将用作其他epoll系统调用的第一个参数&#xff0c;size不起作用。 epoll_ctl函数 #include …

程序设计—房屋租赁系统 项目源码31279

摘 要 在当今社会&#xff0c;随着城市化进程的不断加速&#xff0c;房屋租赁市场的需求日益增长。然而&#xff0c;传统的房屋租赁方式存在信息不对称、租房过程繁琐等问题&#xff0c;因此需要建立一套高效、便捷的房屋租赁系统来满足用户的需求。通过深入研究房屋租赁市场的…

华为 HCIP-Datacom H12-821 题库 (8)

有需要题库的可以看主页置顶 V群仅进行学习交流 1.在 DHCP 运行过程中&#xff0c;如果客户端 IP 地址在相约过去 87.5%还没有完成续约的话&#xff0c;客户将发送什么报文进行再次续约&#xff1f; A、DHCP discover 广播报文 B、DHCP release 单播报文 C、DHCP request 广播…