仿函数 VS 函数指针实现回调

news2025/4/21 2:39:03

前提:

        本博客对比 函数指针实现回调 和  仿函数 ,突出仿函数的优势。

目的:

        一个类要能够灵活的调用两个函数,essfc 和 greaterfc,分别用于比较两个整数的大小:

①:lessfc:判断 x 是否小于 y,如果是则返回 true,否则返回 false

②:greaterfc:判断 x 是否大于 y,如果是则返回 true,否则返回 false

一:函数指针实现回调

①:普通代码:

//lessfc函数 x<y?
bool lessfc(int x, int y)
{
	return x < y;
}

//greaterfc函数 x>y?
bool greaterfc(int x, int y)
{
	return x > y;
}

// A这个类体现了--->函数指针实现回调
class A
{
public:
	A(bool(*pf)(int, int))//构造函数接收一个函数指针 并赋值给成员变量
		:_pf(pf)
	{}

	void func(int xx, int yy)//func函数接收两个参数
	{
		cout << "void func(int xx, int yy)" << _pf(xx, yy) << endl;;
	}   //用接收到的函数指针结合两个int参数调用该函数 

	bool(*_pf)(int, int);//成员变量也是一个函数指针类型
};

int main()
{
	A aa1(lessfc);//创建一个对象
	aa1.func(1, 2);//调用对象的func函数 且传两个int

	A aa2(greaterfc);//创建一个对象
	aa2.func(1, 2);//调用对象的func函数 且传两个int
}

运行结果:

解释:

类 A 的行为可以通过传入不同的函数指针来动态改变:

        传入 lessfc 时,func 方法会执行小于比较。

        传入 greaterfc 时,func 方法会执行大于比较。

②:加上模版的代码:

//lessfc函数 x<y?
template<class T>
bool lessfc(T x, T y)
{
	return x < y;
}

//greaterfc函数 x>y?
template<class T>
bool greaterfc(T x, T y)
{
	return x > y;
}

// A这个类体现了--->函数指针实现回调
template<class T>
class A
{
public:
	A(bool(*pf)(T, T))//构造函数接收一个函数指针 并赋值给成员变量
		:_pf(pf)
	{}

	void func(T xx, T yy)//func函数接收两个参数
	{
		cout << "void func(T xx, T yy)" << _pf(xx, yy) << endl;;
	}   //用接收到的函数指针结合两个int参数调用该函数 

	bool(*_pf)(T, T);//成员变量也是一个函数指针类型
};

int main()
{
	A<int> aa1(lessfc);//因为有模版,所以显式传模版的类型去创建一个对象
	aa1.func(1, 2);//调用对象的func函数 且传两个int 与创建时T的类型吻合

	A<double>aa2(greaterfc);//因为有模版,所以显式传模版的类型创建一个对象
	aa2.func(1.1, 1.0);//调用对象的func函数 且传两个double 与创建时T的类型吻合
}

解释:加上模版之后,范围更广了

二:仿函数

定义:一个对象能像函数一样用,实际上调用的是我们重载的 operator()

①:普通代码:

//定义仿函数
class lessfc //将之前的lessfc函数封装成一个类
{
public:
	//类中仅需对()这个操作符进行重载
	bool operator()(const int& x, const int& y)
	{
		return x < y;
	}
};

//定义仿函数
class greaterfc //将greaterfc函数封装成一个类
{
public:
	//类中仅需对()这个操作符进行重载
	bool operator()(const int& x, const int& y)
	{
		return x > y;
	}
};


template<class Comapre>
class A//A这个类要使用仿函数
{
public:
	void func(int xx, int yy)
	{
		Comapre com;//创建一个 Comapre 类型的对象 com。
		cout << "void func(int xx, int yy)" << com(xx, yy) << endl;;
		//调用 com(xx, yy),即调用仿函数的 operator(),比较 xx 和 yy。
	}
};


int main()
{
	A<less<int>> aa1;
	aa1.func(1, 2); // 输出 1 < 2 的结果(true)

	A<greater<int>> aa2;
	aa2.func(1, 2); // 输出 1 > 2 的结果(false)
}

运行结果:

①:不再像函数指针那样,赤裸裸的写两个函数,而是在两个类中对()运算符进行重载,重载的定义在写函数的功能

②:需要用仿函数的类的模版参数,就是那两个仿函数的类名,在类中进行仿函数类的对象的创建,再结合类中函数(如func)的参数,最后结合()进行函数的调用,仿函数对象看见()就会实现重载后的功能

③:main函数中的less<int>,就是对仿函数进行显式的调用,此时的模版不再是简单的类型,而是一个类

②:加上模版的代码:

//定义仿函数
template<class T>
class lessfc //将之前的lessfc函数封装成一个类
{
public:
	//类中仅需对()这个操作符进行重载
	bool operator()(const T& x, const T& y)
	{
		return x < y;
	}
};

//定义仿函数
template<class T>
class greaterfc //将greaterfc函数封装成一个类
{
public:
	//类中仅需对()这个操作符进行重载
	bool operator()(const T& x, const T& y)
	{
		return x > y;
	}
};


template<class Comapre, class T>
class A//A这个类要使用仿函数
{
public:
	void func(T xx, T yy)
	{
		Comapre com;//创建一个 Comapre 类型的对象 com。
		cout << "void func(T xx, T yy)" << com(xx, yy) << endl;;
		//调用 com(xx, yy),即调用仿函数的 operator(),比较 xx 和 yy。
	}
};


int main()
{
	A<less<int>, int> aa1;
	aa1.func(1, 2); // 输出 1 < 2 的结果(true)

	A<greater<double>, double> aa2;
	aa2.func(1.1, 1.0); // 输出 1.1 >1.0 的结果(ture)
}

运算结果:

解释:对三个类都加上模版之后,范围更广了

三:仿函数更优的点

1. 更灵活的功能扩展

仿函数:仿函数是一个类对象,可以包含成员变量和成员函数,因此可以在调用时保存状态(例如计数器、配置参数等)。

函数指针:函数指针只能指向一个静态函数,无法保存状态,功能扩展性较差。

2. 更高的性能

仿函数:仿函数通常是内联的(inline),编译器可以优化其调用,减少函数调用的开销。

函数指针:函数指针的调用需要通过指针间接调用,无法内联,性能稍差。

3. 更强的类型安全

仿函数:仿函数是类对象,类型信息在编译时确定,类型安全性更高。

函数指针:函数指针的类型信息较弱,容易出错(例如指向错误类型的函数)。


4. 更好的通用性

仿函数:仿函数可以与模板结合,实现高度通用的代码。例如,STL 中的排序算法 std::sort 可以通过传入不同的仿函数实现升序或降序排序。

函数指针:函数指针的通用性较差,难以与模板结合。

5. 支持运算符重载
仿函数:仿函数可以重载 operator(),使得对象可以像函数一样被调用,语法更直观。

函数指针:函数指针的调用语法较为繁琐。

6. 更易于组合和扩展
仿函数:仿函数可以通过继承、组合等方式扩展功能。

函数指针:函数指针的功能扩展性较差,难以实现复杂的逻辑组合。


 

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

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

相关文章

MQTT的安装和使用

MQTT的安装和使用 在物联网开发中&#xff0c;mqtt几乎已经成为了广大程序猿必须掌握的技术&#xff0c;这里小编和大家一起学习并记录一下~~ 一、安装 方式1、docker安装 官网地址 https://www.emqx.com/zh/downloads-and-install/broker获取 Docker 镜像 docker pull e…

PRODIGY: “不折腾人”的蛋白-蛋白/蛋白-小分子结合能计算工具

PRODIGY&#xff08;全称为 PROtein binDIng enerGY prediction&#xff09;是一种蛋白质结合能预测工具&#xff0c;可利用蛋白质-蛋白质复合物的三维结构来预测其结合亲和力。PRODIGY 利用一种高效的基于接触的方法&#xff0c;在估计结合自由能和解离常数的同时&#xff0c;…

C++之 【模板初阶(函数模板与类模板)】

目录 1.泛型编程 2.模板 3函数模板 3.1函数模板的概念 3.2函数模板的格式 3.3函数模板的原理 3.4函数模板的实例化 3.4.1隐式实例化&#xff1a;让编译器根据实参推演模板参数的实际类型 3.4.2显示实例化&#xff1a;在函数名后的<>中指定模板参数的实际类型 3.…

在线教育网站项目第四步:deepseek骗我, WSL2不能创建两个独立的Ubuntu,但我们能实现实例互访及外部访问

一、说明 上一章折腾了半天&#xff0c;搞出不少问题&#xff0c;今天我们在deepseek的帮助下&#xff0c;完成多个独立ubuntu24.04实例的安装&#xff0c;并完成固定ip&#xff0c;实践证明&#xff0c;deepseek不靠谱&#xff0c;浪费我2个小时时间&#xff0c;我们将在下面实…

在刀刃上发力:如何精准把握计划关键节点

关键路径分析是项目管理中的一种重要方法&#xff0c;它通过在甘特图中识别出项目中最长、最关键的路径&#xff0c;来确定项目的最短完成时间。 关键路径上的任务都是项目成功的关键因素&#xff0c;任何延误都可能导致整个项目的延期。关键路径分析对于项目管理者来说至关重要…

组合总和||

1.给定一个数组 candidates 和一个目标数 target &#xff0c;找出 candidates 中所有可以使数字和为 target 的组合。 candidates 中的每个数字在每个组合中只能使用一次。 #include <bits/stdc.h> using namespace std; vector<vector<int>> result; vec…

OpenCV图像拼接(2)基于羽化(feathering)技术的图像融合算法拼接类cv::detail::FeatherBlender

操作系统&#xff1a;ubuntu22.04 OpenCV版本&#xff1a;OpenCV4.9 IDE:Visual Studio Code 编程语言&#xff1a;C11 算法描述 cv::detail::FeatherBlender 是 OpenCV 中用于图像拼接的一个类&#xff0c;它属于 stitching 模块的一部分。这个类实现了基于羽化&#xff08;…

DeepSeek 助力 Vue3 开发:打造丝滑的表格(Table)之添加导出数据功能示例9,TableView15_09带排序的导出表格示例

前言&#xff1a;哈喽&#xff0c;大家好&#xff0c;今天给大家分享一篇文章&#xff01;并提供具体代码帮助大家深入理解&#xff0c;彻底掌握&#xff01;创作不易&#xff0c;如果能帮助到大家或者给大家一些灵感和启发&#xff0c;欢迎收藏关注哦 &#x1f495; 目录 Deep…

spring boot 登入权限RBAC模式

首先准备好5张表 user_info表&#xff0c;用户的信息表 role表&#xff0c;角色表&#xff08;比如超级管理员、管理员、审核员、采购......&#xff09; 创建user_role表&#xff0c;user_info表&#xff0c;role表的中间表 注意了&#xff0c;role_id和user_id是 u…

调用API拿到的值——存储方式

1.响应结果示例&#xff1a; "purposeTagList":["稳中向好及进中提质"] 2.数据库中定义的 3.值的获取&#xff1a; F1 JsonNode purposeTagListNode dataNode.path("purposeTagList");if (purposeTagListNode.isArray()) {StringBuilder purp…

第一讲 | 解锁C++编程能力:基础语法解析

C入门基础 一、C的第一个程序二、命名空间三、C输入&输出四、缺省参数/默认参数五、函数重载六、引用1.引用的特性2.引用的使用引用做返回值场景 3.const引用只有指针和引用涉及权限放大、缩小的问题&#xff0c;普通变量没有 4.指针和引用的关系 七、inline八、nullptr 一…

C++ --- 多态

1 多态的概念 多态(polymorphism)的概念&#xff1a;通俗来说&#xff0c;就是多种形态。多态分为编译时多态(静态多态)和运⾏时多 态(动态多态)&#xff0c;这⾥我们重点讲运⾏时多态&#xff0c;编译时多态(静态多态)和运⾏时多态(动态多态)。编译时 多态(静态多态)主要就是我…

HAL库中使用空闲中断+DMA接收数据,接收失败的问题

问题&#xff1a; 串口屏与单片机通过串口&#xff08;USART1&#xff09;进行通信&#xff0c;调试时发现问题&#xff0c;现象如下&#xff1a; 手动页面的几个文本&#xff0c;输入的数字不会显示出来&#xff0c;比如初始值为0&#xff0c;输入200&#xff0c;200会一闪而…

使用pycel将Excel移植到Python

1.适用需求 有些工作可能长期适用excel来进行公式计算&#xff0c;当需要把工作流程转换为可视化界面时&#xff0c;开发人员不懂专业逻辑&#xff0c;手动摸索公式很大可能出错&#xff0c;而且费时费力 2.可用工具及缺点 pandas 方便进行数据处理&#xff0c;支持各种格…

学习应用层

应用层概述 客户/服务器方式&#xff08;C/S&#xff09;和对等方式&#xff08;P2P&#xff09; 动态主机配置协议DHCP 客户/服务器方式 DHCP报文会被封装成为UDP用户数据报&#xff0c;DHCP服务器端口号是UDP67&#xff0c;用户是UDP68。 广播发送&#xff0c;是因为并不知道…

C/C++转换为字符串宏和字符串拼接宏的综合使用

本文内容参考: C/C++ 宏拼接和宏展开为字符串 - DoubleLi - 博客园 特此致谢! 1. 转换为字符串宏与字符串拼接宏 (1)转换为字符串宏 转换为字符串的宏为: #define STR(x) #x //转字符串 (2)字符串拼接宏 字符串拼接的宏为: #define CONCAT(x,y) x##y //拼接 2…

掌握新编程语言的秘诀:利用 AI 快速上手 Python、Go、Java 和 Rust

网罗开发 &#xff08;小红书、快手、视频号同名&#xff09; 大家好&#xff0c;我是 展菲&#xff0c;目前在上市企业从事人工智能项目研发管理工作&#xff0c;平时热衷于分享各种编程领域的软硬技能知识以及前沿技术&#xff0c;包括iOS、前端、Harmony OS、Java、Python等…

个人常用的chrome好用插件

chrome可以说是兼容性和实用性较高的浏览器 没有复杂的ui 沉重的广告 加上各种各样的浏览器插件 现在罗列一下个人常用的几款好用的插件 1. Adblock Plus 一款免费的广告拦截器&#xff0c;可以拦截大部分网站上的广告推荐&#xff0c;还你一个干净舒服的页面 以下为b站演示…

数据库设计-笔记2

1.介绍一下MySQL 历史与发展 MySQL 最初由瑞典的 MySQL AB 公司开发&#xff0c;于 1995 年正式发布。2008 年&#xff0c;MySQL AB 公司被 Sun Microsystems 收购&#xff0c;之后 Sun 又被甲骨文&#xff08;Oracle&#xff09;公司收购&#xff0c;MySQL 成为 Oracle 旗下…

损失函数理解(二)——交叉熵损失

损失函数的目的是为了定量描述不同模型&#xff08;例如神经网络模型和人脑模型&#xff09;的差异。 交叉熵&#xff0c;顾名思义&#xff0c;与熵有关&#xff0c;先把模型换成熵这么一个数值&#xff0c;然后用这个数值比较不同模型之间的差异。 为什么要做这一步转换&…