C++包装类

C++包装类是为了统一解决一些调用问题,在C++中:“函数指针回调、仿函数、lambda表达式”都属于可调用对象,他们都可以达到函数函数的行为,达到回调函数和事件处理的功能。

但他们各有优缺点:

  1. 函数指针:符合C/C++一贯使用的语法,无需增加过多的语法特性,但是指针写起来过于恶心
  2. 仿函数:很好用,但是用之前需要创建仿函数类,重载运算符()才可以使用,但是命名不好的时候不容易看出仿函数的意义,有的时为了一个小函数创建出一个类过于大刀阔斧了
  3. lambda表达式:书写方便,意义一眼明了,写短小的函数时非常方便,接受参数什么的很方便,但是是一个匿名函数,不容易精确书写出函数类型,甚至同一个书写方式、运作逻辑的lambda表达式在不同环境下都是不同函数类型的

因此就会产生一些问题:

  1. 上面这3种方式实际上都有同一个缺陷,函数的类型都很难写,而有些时候我们需要明确知道函数的类型(例如:在使用map这些类模板时,需要调整对key的认定)
  2. 明明是类似的行为,为什么不可以统一管理呢?
  3. 我们几乎很难确定一个对象ret = func(x)是函数指针?还是仿函数?亦或者lambda表达式?

因此C++新增加了function{};类,这是一个很特殊的类模板。

1.function

实际上function{}包装类是一个适配器,其使用语法和以前的语法有些不一样,甚至可以说怪异的地方。

#include<functional>
template<class Ret, class... Args> 
class function<Ret(Args...)>
{/*...*/};
//Ret 是函数的返回值类型
//Args 是函数参数列表的类型

因此我们可以做出一些统一的封装玩法,这就让三者可以统一起来。

#include <iostream>
#include <string>
#include <map>
#include <functional>
using namespace std;void swap_function(int& r1, int& r2)
{int tmp = r1;r1 = r2;r2 = tmp;
}struct swap_struct
{void operator()(int& r1, int& r2){int tmp = r1;r1 = r2;r2 = tmp;}
};int main()
{int x = 1, y = 10;cout << "x = " << x << ", y = " << y << '\n';auto swap_lambda = [](int& r1, int& r2){int tmp = r1;r1 = r2;r2 = tmp;};function<void(int&, int&)> func_1 = swap_function;func_1(x, y);cout << "x = " << x << ", y = " << y << '\n';function<void(int&, int&)> func_2 = swap_struct();func_2(x, y);cout << "x = " << x << ", y = " << y << '\n';function<void(int&, int&)> func_3 = swap_lambda;func_3(x, y);cout << "x = " << x << ", y = " << y << '\n';map<string, function<void(int&, int&)>> swap = {{ "函数指针", swap_function },{ "仿函数", swap_struct() },{ "lambda", swap_lambda }};swap["函数指针"](x, y);cout << "x = " << x << ", y = " << y << '\n';swap["仿函数"](x, y);cout << "x = " << x << ", y = " << y << '\n';swap["lambda"](x, y);cout << "x = " << x << ", y = " << y << '\n';return 0;
}

当然,达到统一包装的标准是参数类型必须相同,但是我们也有办法解决这个问题,我们有机会再提及。

另外,我们之前做过一道关于逆波兰表达式的题目,我们可以优化一下这道题目的代码。

//没优化前
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;//[capture-list](parameters)mutable->return-type{statement}map<string, function<int(int, int)>> func = {{"+", [](int x, int y)->int{return x + y;}}, {"-", [](int x, int y)->int{return x - y;}}, {"*", [](int x, int y)->int{return x * y;}}, {"/", [](int x, int y)->int{return x / y;}}};for (auto &str : tokens){if (func.count(str))//判断是否为操作符{int right = st.top();st.pop();int left = st.top();st.pop();st.push(func[str](left, right));}else{st.push(stoi(str));}}return st.top();}
};

这样的代码可拓展性更好,更加简洁明了。

另外,我们还需要了解一下成员函数的包装,这里有些特殊的处理。

#include <iostream>
#include <string>
#include <map>
#include <functional>
using namespace std;class Data
{
public:void Function(int x, double y){cout << _data << "-" << x << "-" << y << '\n';}
private:int _data = 10;
};
int main()
{Data d;function<void(Data*, int, double)> f1 = &Data::Function; //对于非静态成员函数必须显示取地址f1(&d, 1, 3.1);//f1(&Data(), 1, 3.1);function<void(Data&, int, double)> f2 = &Data::Function;f2(d, 6, 3.8);//f2(Data(), 6, 3.8);function<void(Data, int, double)> f3 = &Data::Function;f3(d, 5, 1.5);f3(Data(), 5, 1.5);//function<void(const Data*, int, double)> f4 = &Data::Function;//function<void(const Data&, int, double)> f5 = &Data::Function;function<void(const Data, int, double)> f6 = &Data::Function;f6(d, 6, 3.14);f6(Data(), 6, 3.14);return 0;
}

补充:functiuon{};奇特的语法都是C++不曾有的,很多在解释语法的解释器上就做了修改,因此我们只需要学会如何使用即可,其内部底层原理我们不必过于探究。

2.bind

但是上面成员函数的包装中,每次都需要传递一个Data的对象、指针或引用,但这样太麻烦了,我们可以使用bind(),这个函数模板也在头文件functional内。

可以在用某个函数之前绑定某几个参数,然后让控制用户的参数传入。

#include <iostream>
#include <string>
#include <map>
#include <functional>
using namespace std;class Data
{
public: void Print(float x, double y){cout << _data << "-" << x << "-" << y << '\n';}
private:int _data = 10;
};int Add(int x, int y, int z)
{return x + y + z;
}
int main()
{//1.普通函数的绑定function<int(int, int, int)> Add_1 = &Add;cout << Add_1(1, 2, 3) << '\n';function<int(int)> Add_2 = bind(&Add, 1, 2, placeholders::_1);cout << Add_2(3) << '\n';//2.成员函数的绑定Data d;function<void(Data*, float, double)> Print_1 = &Data::Print;Print_1(&d, 2, 3.14);function<void(float, double)> Print_2 = bind(&Data::Print, Data(), placeholders::_2, placeholders::_1);Print_2(2, 3.14);return 0;
}

注意这里的_1_2是根据用户输入的参数来决定的,而不是根据原本的函数参数。

这个bind()还是很有用的,可以让函数的调用由使用者来控制缺省参数,而不是开发者。在调用一些繁琐的系统调用时,这个函数模板就会很有效。

因为有太多系统调用需要传递各种参数了,这些参数又不能直接设置为缺省参数,但是我们又经常需要传递,因此就只能一个一个传,但是有了这个函数模板就可以简化函数调用了。

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

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

相关文章

QT 中 QDateTime::currentDateTime() 输出格式备查

基础 QDateTime::currentDateTime() //当前的日期和时间。 QDateTime::toString() //以特定的格式输出时间&#xff0c;格式 yyyy: 年份&#xff08;4位数&#xff09; MM: 月份&#xff08;两位数&#xff0c;07表示七月&#xff09; dd: 日期&#xff08;两位数&#xff0c…

合成相机模型【图形学】

相机在计算机图形学中有两个方面的考虑&#xff1a;相机的位置和相机的形状。 要了解后者&#xff0c;我们需要了解相机的工作原理。 NSDT工具推荐&#xff1a; Three.js AI纹理开发包 - YOLO合成数据生成器 - GLTF/GLB在线编辑 - 3D模型格式在线转换 - 可编程3D场景编辑器 - R…

如何在Rocky Linux中安装nmon

一、环境基础 [rootlocalhost nmon16d]# cat /etc/redhat-release Rocky Linux release 9.2 (Blue Onyx) [rootlocalhost nmon16d]# uname -r 5.14.0-284.11.1.el9_2.x86_64 [rootlocalhost nmon16d]# 二、安装步骤 在Rocky Linux和AlmaLinux等基于RHEL 的发行版上&#xff…

编程实战:类C语法的编译型脚本解释器(四)

系列入口&#xff1a; 编程实战&#xff1a;类C语法的编译型脚本解释器&#xff08;系列&#xff09;-CSDN博客 本文介绍总入口和使用方法。 一、总入口 class IScript{private:CScript* m_p;string m_msg;public:IScript() :m_p(nullptr) {}string GetMessage()const;//连接bo…

前后端数据传输格式(上)

作者简介&#xff1a;大家好&#xff0c;我是smart哥&#xff0c;前中兴通讯、美团架构师&#xff0c;现某互联网公司CTO 联系qq&#xff1a;184480602&#xff0c;加我进群&#xff0c;大家一起学习&#xff0c;一起进步&#xff0c;一起对抗互联网寒冬 作为后端&#xff0c;写…

C#,数值计算——插值和外推,三次样条插值(Spline_interp)的计算方法与源程序

1 文本格式 using System; namespace Legalsoft.Truffer { /// <summary> /// 三次样条插值 /// Cubic Spline Interpolation /// Cubic spline interpolation object. Construct with x and y vectors, and /// (optionally) values of the first…

C++EasyX之井字棋

视频链接 井字棋 用EasyX和C实现井字棋小游戏 源码及注释 #include<graphics.h>char board_data[3][3] {{-,-,-},{-,-,-},{-,-,-}, };char current_piece O;//检测指定棋子的玩家是否获胜 bool CheckWin(char c) {// 检查每一行for (int i 0; i < 3; i){if (bo…

测试:性能测试

一、性能测试 性能测试是一种评估软件、系统或服务在特定条件下性能的过程。性能测试有助于确定系统的响应时间、吞吐量、可扩展性、稳定性和资源消耗等关键指标。 一、响应时间 响应时间&#xff08;Response Time&#xff09;是性能测试中的一个重要指标&#xff0c;用于衡…

算法学习系列(三):汉诺塔

目录&#xff1a; 引言一、问题描述二、问题求解三、测试四、附录&#xff08;所有代码&#xff09; 引言 这个汉诺塔问题就是一个典型的递归问题&#xff0c;这篇博客也算是上一篇的一个扩展吧&#xff0c;都是递归问题&#xff0c;这个问题太大&#xff0c;而且牵扯到的问题…

深度学习——第03章 Python程序设计语言(3.1 Python语言基础)

无论是在机器学习还是深度学习中&#xff0c;Python已经成为主导性的编程语言。而且&#xff0c;现在许多主流的深度学习框架&#xff0c;例如PyTorch、TensorFlow也都是基于Python。本课程主要是围绕“理论实战”同时进行&#xff0c;所以本章将重点介绍深度学习中Python的必备…

webGIS使用JS,高德API完成智慧校园项目打卡功能

代码实现&#xff1a; var map new AMap.Map(container,{center:[],//目标点的中心位置zoom:16,viewMode:3D,pitch:45,})//使用控件AMap.plugin([AMap.ToolBar,AMap.Scale,AMap.ControlBar,AMap.HawkEye,AMap.MoveAnimation],function(){map.addControl(new AMap.ToolBar({ pos…

【Java面试——JUC全局观、原子类、锁、集合类、线程池、工具类】

目录 3.3 JUC全局观 JUC框架包含几个部分? Lock框架和Tools哪些核心的类? JUC并发集合哪些核心的类? JUC原子类哪些核心的类? JUC线程池哪些核心的类? 3.4 JUC原子类 线程安全的实现方法有哪些? 什么是CAS? CAS使用示例&#xff0c;结合AtomicInteger给出示例? CAS会有…

Python遥感开发之快速判断TIF数据为空

Python遥感开发之快速判断TIF数据为空 前言&#xff1a;介绍一下如何使用python下的gdal读取tif数据的时候&#xff0c;快速判断该tif数据是否为空&#xff0c;如果为空的话就把当前的tif删掉。 如图所示&#xff0c;通过arcgis查看箭头指向的为空值。 仅通过文件的大小无法判…

NVMe Over Fabrics with iRDMA总结 - 1

1.0 Introduction简介 NVM Express* (NVMe*) drives are high-speed, low-latency, solid-state drives (SSDs), that connect over the server Peripheral Component Interconnect Express* (PCIe*) bus. NVM Express* (NVMe*) 硬盘是高速、低延迟的固态硬盘 (SSD),通过服务…

人工智能中的模型评估

1 概述 1.1 定义 人工智能&#xff08;AI&#xff09;模型评估是一个关键的过程&#xff0c;用于确定模型在特定任务上的性能和有效性。这个过程涉及使用各种技术和指标来衡量模型的准确度、可靠性、泛化能力以及其他重要特性。在不同的应用场景中&#xff0c;模型评估的具体…

Qt Creator 11.0.3同时使用Qt6.5和Qt5.14.2

Qt Creator 11.0.3同时使用Qt6.5和Qt5.14.2 概要方法1.打开Qt Creator中的Kit&#xff0c;这里我直接附上几张截图&#xff0c;不同的版本打开位置可能有所不同&#xff0c;总之最终目的是要打开构建套件&#xff08;Kit&#xff09;2.可以看到构建套件里面有包含了“构建套件K…

深度学习记录--计算图(前向后向传播)

什么是计算图&#xff1f; 从一个例子入手&#xff1a; 将函数J的计算用流程图表示出来&#xff0c;这样的流程图被称为计算图 简单来说&#xff0c;计算图是用来显示每个变量间的关系的一种图 两种传播方式 计算图有两种传播方式&#xff1a;前向传播 和 后向传播 什么是前…

使用dirmap命令行时报错,提示缺少gevent模块

记得以前是可以的&#xff0c;可能是时间长了重装了系统&#xff0c;引起的。 修复方法。升级pip&#xff0c;然后重新下载安装gevent模块。 具体&#xff1a; python -m pip install --upgrade pip 使用下面命令解决下载慢的问题。 pip config set global.index-url http…

Drools 7 ObjectDataCompiler

ObjectDataCompiler 是用于将一个java 对象填充到template 内生成drl。 实际是转为String对象&#xff0c;父类为DataProviderCompiler要注意对象值为空的处理&#xff0c;如果填充遇到null&#xff0c;会导致rule很奇怪。 测试java 代码如下 public class ObjectDataCompil…

【WPF.NET开发】WPF.NET桌面应用开发概述

本文内容 为何从 .NET Framework 升级使用 WPF 进行编程标记和代码隐藏输入和命令控件布局数据绑定图形和动画文本和版式自定义 WPF 应用 Windows Presentation Foundation (WPF) 是一个与分辨率无关的 UI 框架&#xff0c;使用基于矢量的呈现引擎&#xff0c;构建用于利用现…