浅析C++仿函数

浅析C++仿函数
1.为什么要有仿函数
我们先从⼀个⾮常简单的问题⼊⼿。假设我们现在有⼀个数组,数组中存有任意数量的数字,我们希望能够计数出这个数组中⼤于10的数字的数量,你的代码很可能是这样的:
#include <iostream>
using namespace std;
int RecallFunc(int *start, int *end, bool (*pf)(int))
{
int count=0;
for(int *i=start;i!=end+1;i++)
{
count = pf(*i) ? count+1 : count;
}
return count;
}
bool IsGreaterThanTen(int num)
{
return num>10 ? true : false;
}
int main()
{
int a[5] = {10,100,11,5,19};
int result = RecallFunc(a,a+4,IsGreaterThanTen);
cout<<result<<endl;
return 0;
}
RecallFunc()函数的第三个参数是⼀个函数指针,⽤于外部调⽤,⽽IsGreaterThanTen()函数通常也是外部已经定义好的,它只接受⼀个参数的函数。如果此时希望将判定的阈值也作为⼀个变量传⼊,变为如下函数就不可⾏了:
bool IsGreaterThanThreshold(int num, int threshold)
{
return num>threshold ? true : false;
}
虽然这个函数看起来⽐前⾯⼀个版本更具有⼀般性,但是它不能满⾜已经定义好的函数指针参数的要求,因为函数指针参数的类型是bool (*)(int),与函数bool IsGreaterThanThreshold(int num, int threshold)的类型不相符。如果⼀定要完成这个任务,按照以往的经验,我们可以考虑如下可能途径:
(1)阈值作为函数的局部变量。局部变量不能在函数调⽤中传递,故不可⾏;
(2)函数传参。这种⽅法我们已经讨论过了,多个参数不适⽤于已定义好的RecallFunc函数。
月亮上有什么(3)全局变量。我们可以将阈值设置成⼀个全局变量。这种⽅法虽然可⾏,但是不优雅,且⾮常容易引⼊Bug,⽐如全局变量容易同名,造成命名空间污染。二次革命论
那么有什么好的处理⽅法呢?仿函数应运⽽⽣。
2.仿函数的定义
仿函数(Functor)⼜称为函数对象(Function Object)是⼀个能⾏使函数功能的类。仿函数的语法⼏乎和我们普通的函数调⽤⼀样,不过作为仿函数的类,都必须重载operator()运算符。因为调⽤仿函数,实际上就是通过类对象调⽤重载后的operator()运算符。
如果编程者要将某种“操作”当做算法的参数,⼀般有两种⽅法:
(1)⼀个办法就是先将该“操作”设计为⼀个函数,再将函数指针当做算法的⼀个参数。上⾯的实例就是该做法;
(2)将该“操作”设计为⼀个仿函数(就语⾔层⾯⽽⾔是个class),再以该仿函数产⽣⼀个对象,并以此对象作为算法的⼀个参数。
很明显第⼆种⽅法会更优秀,原因也在上⼀⼩节有所阐述。正如上⾯的例⼦,在我们写代码时有时会发现有些功能代码,会不断地被使⽤。为了复⽤这些代码,实现为⼀个公共的函数是⼀个解决⽅法。不过函数⽤到的⼀些变量,可能是公共的全局变量。引⼊全局变量,容易出现同名冲突,不⽅便维护。
这时就可以⽤仿函数了,写⼀个简单类,除了维护类的基本成员函数外,只需要重载operator()运算符。这样既可以免去对⼀些公共变量的维护,也可以使重复使⽤的代码独⽴出来,以便下次复⽤。⽽且相对于函数更优秀的性质,仿函数,还可以进⾏
依赖、组合与继承等,这样有利于资源的管理。如果再配合模板技术和Policy编程思想,那就更是威⼒⽆穷了,⼤家可以慢慢体会。Policy表述了泛型函数和泛型类的⼀些可配置⾏为(通常都具有被经常使⽤的缺省值)。
STL中也⼤量涉及到仿函数,有时仿函数的使⽤是为了函数拥有类的性质,以达到安全传递函数指针、依据函数⽣成对象、甚⾄是让函数之间有继承关系、对函数进⾏运算和操作的效果。⽐如STL中的容器set就使⽤了仿函数less ,⽽less继承的binary_function,就可以看作是对于⼀类函数的总体声
明了,这是函数做不到的。
//less的定义
template<typename _Tp> struct less : public binary_function<_Tp, _Tp, bool>
{
bool operator()(const _Tp& __x, const _Tp& __y) const
{ return __x < __y; }
};
//set的申明
template<typename _Key,
typename _Compare = std::less<_Key>,
typename _Alloc = std::allocator<_Key>>
class set;
仿函数中的变量可以是static的,同时仿函数还给出了static的替代⽅案,仿函数内的静态变量可以改成类的私有成员,这样可以明确地在析构函数中清除所⽤的内容,如果⽤到了指针,那么这个是不错的选择。有⼈说这样的类已经不是仿函数了,但其实,封装后从外界观察,可以明显地发现,它依然有函数的性质。
3.仿函数实例
我们先来看⼀个仿函数的例⼦:
#include <iostream>
#include <string>
using namespace std;
class Functor
{
public:
void operator() (const string& str) const
{
cout << str << endl;
}
};
int main()
{
Functor myFunctor;
myFunctor("Hello world!");
return 0;
何鲁丽同志简历
}
程序输出:
环糊精Hello world!。
可以见到,仿函数提供了第四种解决⽅案:成员变量。成员函数可以很⾃然的访问成员变量,从⽽解决上⽂最开始的那个问题。
class StringAppend
{
public:
explicit StringAppend(const string& str) : ss(str){}
void operator() (const string& str) const
{
cout<<str<<' '<<ss<<endl;
}
private:
const string ss;
};
int main()
{
StringAppend myFunctor2("and world!");
myFunctor2("Hello");
return 0;
}
程序输出:
vb图书管理系统
Hello and world!。
这个例⼦应该可以让您体会到仿函数的⼀些作⽤:它既能像普通函数⼀样传⼊给定数量的参数,还能存储或者处理更多我们需要的有⽤信息。于是本⼩节开头的问题就迎刃⽽解了:
#include <iostream>
using namespace std;
class IsGreaterThanThresholdFunctor
{
public:
explicit IsLessThanTenFunctor(int tmp_threshold) : threshold(tmp_threshold{}
bool operator() (int num) const
{
return num>10 ? true : false;
}
private:
const int threshold;
};
int RecallFunc(int *start, int *end, IsGreaterThanThresholdFunctor myFunctor)
{
int count=0;
for(int *i=start;i!=end+1;i++)
{
count = myFunctor(*i) ? count+1 : count;
}
return count;
抗原表位
}
int main()
{
int a[5] = {10,100,11,5,19};
int result = RecallFunc(a,a+4,IsLessThanTenFunctor(10));
cout<<result<<endl;
return 0;
}
以上就是浅析C++ 仿函数的详细内容,更多关于C++ 仿函数的资料请关注其它相关⽂章!

本文发布于:2024-09-21 21:41:28,感谢您对本站的认可!

本文链接:https://www.17tex.com/xueshu/435031.html

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。

留言与评论(共有 0 条评论)
   
验证码:
Copyright ©2019-2024 Comsenz Inc.Powered by © 易纺专利技术学习网 豫ICP备2022007602号 豫公网安备41160202000603 站长QQ:729038198 关于我们 投诉建议