仿函数及其应用
最编程
2024-01-12 18:21:43
...
-
仿函数(functor)
仿函数(functor),就是使一个类的使用看上去像一个函数。其实现就是类中实现一个operator(),这个类就有了类似函数的行为,就是一个仿函数类了。
仿函数的概念与作用
在我们写代码时有时会发现有些功能实现的代码,会不断的在不同的成员函数中用到,但是又不好将这些代码独立出来成为一个类的一个成员函数。但是又很想复用这些代码。写一个公共的函数,可以,这是一个解决方法,不过函数用到的一些变量,就可能成为公共的全局变量,再说为了复用这么一片代码,就要单立出一个函数,也不是很好维护。这时就可以用仿函数了,写一个简单类,除了那些维护一个类的成员函数外,就只是实现一个operator(),在类实例化时,就将要用的,非参数的元素传入类中。这样就免去了对一些公共变量的全局化的维护了。又可以使那些代码独立出来,以便下次复用。而且这些仿函数,还可以用关联,聚合,依赖的类之间的关系,与用到他们的类组合在一起,这样有利于资源的管理(这点可能是它相对于函数最显著的优点了)。如果在配合上模板技术和policy编程思想,那就更是威力无穷了,大家可以慢慢的体会。
有时仿函数的使用是为了函数拥有类的性质,以达到安全传递函数指针,依据函数生成对象,甚至是让函数之间有继承关系,对函数进行运算和操作的效果。比如set就使用了仿函数less ,而less继承的binary_function,就可以看作是对于一类函数的总体声明了,这是函数做不到的。
1
2
3
4
5
6
7
8
9
|
//less的定义 template < typename _Tp>
struct less : public binary_function<_Tp, _Tp, bool >
{
bool
operator()( const _Tp& __x, const _Tp& __y) const
{ return __x < __y; }
};
|
1
2
3
4
|
//set的定义 template < typename _Key, typename _Compare = std::less<_Key>,
typename _Alloc = std::allocator<_Key> >
class set;
|
仿函数还给出了static的替代方案,函数内的静态变量可以改成类的私有成员,这样可以明确地在析构函数中清除所用的内容,如果用到了指针,那么这个是不错的选择。有人说这样的类已经不是仿函数了,但其实,封装后从外界观察,可以明显地发现,它依然有函数的性质。
面向对象能够减少代码的耦合性,同样仿函数也沾了class的光。比如,一个dfs()函数,调用的时候要传入位置、深度两个值。从外部观察,dfs(x,1)的语句中,1的意义并不明确,从实际来讲,也的确没有传入的必要,甚至可能导致错误。
一般的解决方案有这样几种:
1、void dfs(int x,int deep=1){...}这样的话,虽然dfs(x)变成了可用语句,但你不能要求每个调用它的人都只传一个参。如果有人写dfs(x,-9999),可能会导致运行错误。
2、void dfs2(int x,int deep){}void dfs(int x){dfs2(x,1);}同样dfs(x)也是可用的,但是如果另一个使用者并不知道dfs与dfs2的区别,写了dfs2(x,-1)还是有风险
3、namespace 某个名字{void dfs2(int x,int deep){...}}void dfs(int x){某个名字::dfs2(x,1);}这样已经不错了,但是namespace的意义不明,其它使用者看到大纲估计会在心中把你千刀万剐。
4、使用仿函数,把dfs()写成仿函数,把2中的dfs2变成它的私有成员函数,这样不会有意义不明的东西出现,也能实现安全调用,从外部看,这就是一个可以“生活自理”、有“独立意识”函数了。
仿函数在各编程语言中的范例
C语言使用函数指针和回调函数来实现仿函数,例如一个用来排序的函数可以这样使用仿函数
#include <stdlib.h> /* Callback function */ int compare_ints_function(void*A,void*B) { return*((int*)(A))<*((int*)(B)); } /* Declaration of C sorting function */ void sort(void*first_item,size_t item_size,void*last_item,int(*cmpfunc)(void*,void*)); int main(void) { int items[]={4,3,1,2}; sort((void*)(items),sizeof(int),(void*)(items +3), compare_ints_function); return 0; }
C++
在C++里,我们通过在一个类中重载括号运算符的方法使用一个函数对象而不是一个普通函数。
class compare_class{ public: bool operator()(int A, int B)const{return A < B;} }; // Declaration of C++ sorting function. template<class ComparisonFunctor> void sort_ints(int* begin_items, int num_items, ComparisonFunctor c); int main(){ int items[]={4, 3, 1, 2}; compare_class functor; sort_ints(items, sizeof(items)/sizeof(items[0]), functor); }
C#
C#是通过委托(delegate)来实现仿函数的。
Java
Java中的仿函数是通过实现包含单个函数的接口实现的
List<String> list =Arrays.asList("10", "1", "20", "11", "21", "12"); Comparator<String> numStringComparator =new Comparator<String>(){ publicint compare(String o1, String o2){ returnInteger.valueOf(o1).compareTo(Integer.valueOf(o2)); } }; Collections.sort(list, numStringComparator);
仿函数的实际应用C++
编辑#include <iostream> #include <algorithm> #include <cstdio> #include <ctime> #include <cstring> #include <string> #include <set> typedef long long LL; class Prt{ char c[53]; int top; public: template <class T> Prt& operator()(T x); Prt& operator()(LL x); Prt& operator()(int x); Prt& operator()(char x); Prt& operator()(const char*x); Prt& operator()(double x); Prt& operator()(const std::string x); Prt& operator()(double x,int k){ sprintf(c,"%%.%dlf",k); printf(c,x); return *this; } }; template <typename T> Prt& Prt::operator()(T x){ std::cout<<x; return *this; } Prt& Prt::operator()(LL x){ if(x==0){ putchar('0'); return *this; } if(x<0){ putchar('-'); x=-x; } top=0; while(x>0){ c[top++]='0'+x%10; x/=10; } while(top--){ putchar(c[top]); } return *this; } Prt& Prt::operator()(int x){ return operator()((LL)(x)); } Prt& Prt::operator()(char x){ putchar(x); return *this; } Prt& Prt::operator()(const char*x){ printf("%s",x); return *this; } Prt& Prt::operator()(double x){ printf("%lf",x); return *this; } Prt& Prt::operator()(const std::string x){ return operator()(x.data()); } Prt prt; struct st{int x,y;st(){x=y=0;}st(int a,int b){x=a;y=b;}}; std::ostream& operator<<(std::ostream& fout,const st&x){ fout<<"["<<x.x<<","<<x.y<<"]"; return fout; } int main(){ prt(">>> prt(\"LL:\")(12341231234123ll)(\' \')(\"int:\")(15)(\'\\n\');\n"); prt("LL:")(12341231234123ll)(' ')("int:")(15)('\n'); prt('\n'); prt(">>> prt(\"char:\")(\'a\')(\" char*(/string):\")(std::string(\"abc\"))(\" d \")\ ((std::string(\"abc\")).data())(\'\\n\');\n"); prt("char:")('a')(" char*(/string):")(std::string("abc"))(" d ") ((std::string("abc")).data())('\n'); prt('\n'); prt(">>> prt(\"double:\")(1.5)(\" double[int]:\")(10.12349746192736,4)(\'\\n\');\n"); prt("double:")(1.5)(" double[int]:")(10.12349746192736,4)('\n'); prt("\n>>> prt(st(12,31));\n"); prt(st(12,31)); prt('\n'); return 0; }
这里放一下输出,这样的安排主要是为了突出代码效果
>>> prt("LL:")(12341231234123ll)(' ')("int:")(15)('\n');
LL:12341231234123 int:15
>>> prt("char:")('a')(" char*(/string):")(std::string("abc"))(" d ")((std::string("abc")).data())('\n');
char:a char*(/string):abc d abc
>>> prt("double:")(1.5)(" double[int]:")(10.12349746192736,4)('\n');
double:1.500000 double[int]:10.1235
>>> prt(st(12,31));
[12,31]
上一篇: 理解C++中的仿函数(或者叫做函数对象)
下一篇: C++仿函数
推荐阅读
-
C++ 仿函数外设和封装器 - 绑定函数
-
8 款采用 HTML5 动画制作的超酷仿苹果应用程序
-
35 岁实现财务*,腾讯程序员手握2300万提前退休?-1000万房产、1000万腾讯股票、加上300万的现金,一共2300万的财产。有网友算了一笔账,假设1000万的房产用于自住,剩下1300万资产按照平均税后20-50万不等进行计算,大约花上26-60年左右的时间才能赚到这笔钱。也就是说,普通人可能奋斗一辈子,才能赚到这笔钱。在很多人还在为中年危机而惶惶不可终日的时候,有的人的35岁,就已经安全着陆,试问哪个打工人不羡慕?但问题是有这样财富积累必然有像样的实力做靠山。没有人可以不劳而获。 看到这里,肯定有人说,那么对于普通人来说,卷可能真就成了唯一的出路。但是卷也有轻松的卷,“偷懒”的卷法,对于程序员而言,刨除掉一时无法改掉的开会传统占用的大部分时间,如何把有限的时间和精力放在真正重要的架构设计、需求设计上,而不是重复的造*,编码、改bug、手动测试。因此在科技改变生活的今天,学会使用AI工具成为程序员们的必备技能。 以全栈式全自动的软件开发工具飞算SoFlu软件机器人为例,作为全球首款面向微服务架构设计和最佳实践的软件机器人,SoFlu软件机器人改变了原来手工编码的作业模式,通过可视化拖拽方式以及参数配置就能实现等同于编写复杂代码的业务逻辑,在设计业务逻辑时就完成了微服务应用开发,做到“业务即图,图即代码”,实现“软件开发,十倍提效”。 而作为飞算SoFlu软件机器人的一个重要组成部分,FuncGPT(慧函数)支持所有类型函数创建。通过自然语言描述Java函数需求,实时生成高质量、高可读性的Java函数代码。生成代码可直接复制到IDEA,或一键导入Java全自动开发工具函数库。 FuncGPT(慧函数)具有五大核心能力: ● 自然语言:通过自然语言即可生成函数,降低软件开发门槛。 ● 秒级函数开发:全自动秒级开发函数,秒级完成,效率千倍提升。 ● 一站式函数开发应用:函数生成即可直接应用,效率、安全有保障。 ● 质量可靠:生成的函数代码符合业界规范、具备良好的可读性,符合最优解。 ● 灵活生成:可以根据开发人员的具体需求生成并支持调整和修改。 以“将文件流转换为File对象”这个需求为例,以下是FuncGPT(慧函数)秒级生成的代码: / 类名:FileConverter // 函数名:convertStreamToFile // 函数功能:将文件流转换为File对象 // POM依赖包:无 import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; public class FileConverter { /** * 将文件流转换为File对象 * @param inputStream 文件流 * @param filePath 文件路径 * @param fileName 文件名 * @return 转换后的File对象 * @throws IOException 如果发生I/O异常 */ public static File convertStreamToFile(InputStream inputStream, String filePath, String fileName) throws IOException { File file = new File(filePath + File.separator + fileName); // 创建File对象 try (FileOutputStream outputStream = new FileOutputStream(file)) { // 创建文件输出流 byte buffer = new byte[1024]; int bytesRead; while ((bytesRead = inputStream.read(buffer)) != -1) { // 从文件流读取数据并写入文件 outputStream.write(buffer, 0, bytesRead); } } return file; // 返回转换后的File对象 } } // 函数示例 // 将文件流转换为File对象示例 // 入参:inputStream,文件流 // 入参:filePath,文件路径 // 入参:fileName,文件名 // 出参:file,转换后的File对象 // 调用示例: // InputStream inputStream = new FileInputStream("example.txt"); // String filePath = "C:\\Users\\User\\Documents"; // String fileName = "example.txt"; // File file = FileConverter.convertStreamToFile(inputStream, filePath, fileName); // System.out.println(file.getAbsolutePath); // 输出结果:例如,将文件流转换为File对象后,文件的绝对路径为:C:\Users\User\Documents\example.txt // 则输出结果为:C:\Users\User\Documents\example.txt 通过分析,不难发现以上代码:
-
贪婪算法在 Python、JavaScript、Java、C++ 和 C# 中的多种实现及其在硬币变化、分数骑士、活动选择和使用哈夫曼编码的最小生成树问题中的应用实例
-
孪生网络入门(上) 连体网及其损耗函数
-
孪生网络入门(上) 连体网络及其损失函数-3 损失函数
-
连体网络孪生神经网络及其在医疗场景中的应用
-
uniapp+vue3聊天室|uni-app+vite4+uv-ui跨端仿微博应用聊天语音/朋友圈
-
数据结构:树的分类及其在数据库索引中的应用
-
2022 张宇 考试基础 30 讲 第 9 讲 单项式函数积分的几何应用