仿函数C++-创新互联
为什么我们需要使用仿函数(仿函数解决了什么痛点)?
成都创新互联2013年至今,先为罗平等服务建站,罗平等地企业,进行企业商务咨询服务。为罗平企业网站制作PC+手机+微官网三网同步一站式服务解决您的所有建站问题。仿函数的优点和作用?
文章目录- 仿函数
- 为什么需要仿函数
- 场景一
- 场景二
- 仿函数是什么
- 具体定义
- 实例
- 直接调用仿函数
- 仿函数作为函数入参
- 仿函数和STL
- 算术仿函数
- 关系运算符
- 逻辑运算符
- 仿函数和智能指针
- 仿函数自定义删除器
- 智能指针中的仿函数
场景引入
计算a数组里面全部的和,放在x变量里面。
#include#include#include
using namespace std;
templateinline T accumulate(InputIterator first, InputIterator last, T init, T (*ptrA)(T, T)) {//函数指针
while (first != last) {init = (*ptrA)(init, *first);
++first;
}
return init;
}
int funcA(int x, int y)
{return x + y;
}
int main(void)
{int a[5] = {2, 5, 7, 9, 11};
random_shuffle(&a[0], &a[5]);
int x = ::accumulate(&a[0], &a[5], 0, funcA);
cout<< x<< endl;
return 0;
}
- 这里就是计算a数组里面全部的和,放在x变量里面。
- 首先,这个函数的原型是T (*ptrA)(T x, T y);如果我想提升我处理数据的速度,让时间少浪费由于函数值传递引起的拷贝上,于是我需要这样写函数 T funcA(const int& x, const int& y)。这样写的话,我们原来的函数T (*ptrA)(T x, T y)就不能匹配这个函数,于是我们就要重载函数。还有就是效率问题,函数指针的调用,我们的电脑需要做很多工作,比如说保存当前的寄存器值,传递参数,返回值,返回到函数调用地方继续执行等。
- 幸运的是,这一切问题都可以通过仿函数来解决,如下:
#include#include#include
using namespace std;
templateinline T accumulate(InputIterator first, InputIterator last, T init, FunObject object) {//函数对象
while (first != last) {init = object(init, *first);
++first;
}
return init;
}
template< typename T>class Test {public:
T operator()(const T& x, const T& y) {return x + y;
}
};
int main(void)
{int a[5] = {2, 5, 7, 9, 11};
random_shuffle(&a[0], &a[5]);
int x = ::accumulate(&a[0], &a[5], 0, Test()); //仿函数作为函数的入参,只需要传入类对象即可,这里传入的是匿名对象
cout<< x<< endl;
return 0;
}
这样就解决了效率和函数重载的问题了。
场景二- 假设我们现在有一个数组,数组中存有任意数量的数字,我们希望能够统计出这个数组中大于 10 的数字的数量,你的代码很可能是这样的:
#includeusing 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<
- RecallFunc() 函数的第三个参数是一个函数指针,用于外部调用,而 IsGreaterThanTen() 函数通常也是外部已经定义好的,它只接受一个参数的函数。如果此时希望将判定的阈值也作为一个变量传入,变为如下函数:
bool IsGreaterThanThreshold(int num, int threshold) {return num>threshold ? true : false;
}
- 虽然这个函数看起来比前面一个版本更具有一般性,但是它不能满足已经定义好的函数指针参数的要求,因为函数指针参数的类型是bool (*)(int),与函数bool IsGreaterThanThreshold(int num, int threshold)的类型不相符。如果一定要完成这个任务,按照以往的经验,我们可以考虑如下可能途径:
(1)阈值作为函数的局部变量。局部变量不能在函数调用中传递,故不可行;
bool IsGreaterThanThreshold(int num) {int threshold; // 这里的threhold 没法获得外部的传参
return num>threshold ? true : false;
}
(2)全局变量。我们可以将阈值设置成一个全局变量。这种方法虽然可行,但不优雅,且容易引入 Bug,比如全局变量容易同名,造成命名空间污染
int threshold=10; // 定义全局变量
bool IsGreaterThanThreshold(int num) {
return num>threshold ? true : false;
}
(3)函数传参。这种方法我们已经讨论过了,多个参数不适用于已定义好的 RecallFunc() 函数。(除非你重写函数指针)
假设你设计的传参函数是这样的:
bool IsGreaterThanThreshold(int num, int threshold) {
return num>threshold ? true : false;
}
在此基础上,你必须重写 RecallFunc() 函数 。
int RecallFunc(int *start, int *end, bool (*pf)(int,int),int threshold) {这里就需要引入新参数,来指threshold。同时需要重写函数指针,以使其符合IsGreaterThanThreshold 。
int count=0;
for(int *i = start; i != end+1; i++) {count = pf(*i,threshold) ? count+1 : count;
}
return count;
}
这种方法扩展性较差,当函数参数有所变化,则无法兼容旧的代码,具体在第一小节已经阐述。正如上面的例子,在我们写代码时有时会发现有些功能代码,会不断地被使用。为了复用这些代码,实现为一个公共的函数是一个解决方法。不过函数用到的一些变量,可能是公共的全局变量。引入全局变量,容易出现同名冲突,不方便维护。
这时就可以使用仿函数了,写一个简单类,除了维护类的基本成员函数外,只需要重载 operator() 运算符 。这样既可以免去对一些公共变量的维护,也可以使重复使用的代码独立出来,以便下次复用。而且相对于函数更优秀的性质,仿函数还可以进行依赖、组合与继承等,这样有利于资源的管理。如果再配合模板技术和 Policy 编程思想,则更加威力无穷,大家可以慢慢体会。Policy 表述了泛型函数和泛型类的一些可配置行为(通常都具有被经常使用的缺省值)。
STL 中也大量涉及到仿函数,有时仿函数的使用是为了函数拥有类的性质,以达到安全传递函数指针、依据函数生成对象、甚至是让函数之间有继承关系、对函数进行运算和操作的效果。比如 STL 中的容器 set 就使用了仿函数 less ,而 less 继承的 binary_function,就可以看作是对于一类函数的总体声明,这是函数做不到的。
- 仿函数实现
#includeusing namespace std;
class IsGreaterThanThresholdFunctor {public:
explicit IsGreaterThanThresholdFunctor(int t):threshold(t){}
bool operator() (int num) const {return num >threshold ? 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, IsGreaterThanThresholdFunctor(10));//仿函数作为函数的入参,只需要传入类对象即可,这里传入的是匿名对象
cout<< result<< endl;
}
- 这个例子应该可以让您体会到仿函数的一些作用:它既能像普通函数一样传入给定数量的参数,还能存储或者处理更多我们需要的有用信息。于是仿函数提供了第四种解决方案:成员变量。成员函数可以很自然地访问成员变量,从而可以解决第一节“1.为什么要有仿函数”中提到的问题:计算出数组中大于指定阈值的数字数量。
仿函数(Functor)又称为函数对象(Function Object)是一个能行使函数功能的类。
仿函数的语法几乎和我们普通的函数调用一样,不过作为仿函数的类,都必须重载 operator() 运算符。因为调用仿函数,实际上就是通过类对象调用重载后的 operator() 运算符。
我们先来看一个仿函数的例子。
直接调用仿函数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");// 隐式写法
// myFunctor2.operator()("Hello"); //显式写法
编译运行输出:
Hello and world!
仿函数作为函数入参#includeusing namespace std;
class IsGreaterThanThresholdFunctor {public:
explicit IsGreaterThanThresholdFunctor(int t):threshold(t){}
bool operator() (int num) const {return num >threshold ? 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, IsGreaterThanThresholdFunctor(10));//仿函数作为函数的入参,只需要传入类对象即可,这里传入的是匿名对象
cout<< result<< endl;
}
仿函数和STL通过仿函数建立与stl沟通的桥梁,只为算法服务,当我需要对算法提出一些要求的时候,例如排序默认为从小到大,但是我需要由大到小进行排序,就需要用一般函数的形式或仿函数的形式告诉算法,实现第二个版本的算法。
为什么要把加减,比大小等等功能定义成一个函数或仿函数,因为需要将这些信息传入算法中。算法拿到这些东西之后才会做出相对应的改变。
STL内建的算术类仿函数,支持加法、减法、乘法、除法、模数(余数)、否定运算。
templateT plus //加法仿函数
templateT minus //减法仿函数
templateT multiplies //乘法仿函数
templateT divides //除法仿函数
templateT modulus //取模仿函数
templateT negate //取反仿函数
struct plus : public binary_function{T operator()(const T &x, const T &y) const return x y;
};
templatestruct minus : public binary_function{T operator()(const T &x, const T &y) const return x - y;
};
templatestruct multiplies : public binary_function{T operator()(const T &x, const T &y) const return x y;
};
templatestruct divides : public binary_function{T operator()(const T &x, const T &y) const return x y;
};
templatestruct modulus : public binary_function{T operator()(const T &x, const T &y) const return x $y;
};
templatestruct negate : public unary_function{T operator()(const T &x) const return -x;
};
- 例子
#include#includeusing namespace std;
void test1()
{negaten;
cout<< n(50)<< endl;
}
void test2()
{plusp;
cout<< p(10, 20)<< endl;
}
int main()
{test1();
test2();
std::cout<< "Hello World!\n";
}
// -50
// 30
关系运算符STL支持6种关系运算,每一种都是二元运算
等于,不等于,大于,大于等于,小于,小于等于
templatestruct equal_to:public binary_function{bool operator()(const T&x,const T& y)const {return x==y;};
}
templatestruct not_equal_to:public binary_function{bool operator()(const T& x,const T& y)const {return x!=y;}
};
templatestruct greater:public binary_function{bool operator()(const T&x ,const T7 y)const {return x>y;}
};
templatestruct less:public binary_function{bool operator()(const T&x ,const T7 y)const {return xstruct greater_equal:public binary_function{bool operator()(const T&x ,const T7 y)const {return x>=y;}
};
templatestruct less_equal:public binary_function{bool operator()(const T&x ,const T7 y)const {return x<=y;}
};
逻辑运算符templatebool logical_and//逻辑与
templatebool logical_or //逻辑或
templatebool logical_not//逻辑非
仿函数和智能指针
仿函数自定义删除器//用来释放malloc出来的函数对象
templateclass FreeFunc{public:
void operator()(T* ptr)
{cout<< "free:"<< ptr<< endl;
free(ptr);
}
};
//用来释放new[]出来的函数对象
templateclass DeleteArrayFunc {public:
void operator()(T* ptr)
{cout<< "delete[]"<< ptr<< endl;
delete[] ptr;
}
};
//用来释放new 出来的函数对象
templateclass DeleteArrayFunc {public:
void operator()(T* ptr)
{cout<< "delete "<< ptr<< endl;
delete ptr;
}
};
//用来释放文件描述符的函数对象
templateclass ClosefdFunc{public:
void operator()(T* fd)
{cout<< "close fd"<< fd<< endl;
fclose(fd);
}
};
void test06(){FreeFuncObject1;
shared_ptr sp1((int*)malloc(sizeof(int)*4), Object1); // 回调函数是可调用对象,可以是普通的函数名或者函数对象或者lambda表达式
DeleteArrayFuncObject2;
shared_ptr sp2(new int[4], Object2);
ClosefdFuncObject3;
shared_ptr sp3(fopen("myfile.txt","w"), Object3);
}
int main()
{test06();
return 0;
}
- 输出结果:
close fd0x7ff94b4bfa90
delete[]0x220c21d1ae0
free:0x220c21d1770
智能指针中的仿函数//智能指针的删除器:
templatestruct default_delete{//......
//default deleter for unique_ptr,其中_Ptr是智能指针底层资源的指针
void operator()(_Ty *_Ptr) const _NOEXCEPT
{// delete a pointer
delete _Ptr;
//默认删除器仅仅只做一件事,只调用delete进行资源的释放
}
//......
};
改进,针对这种特殊的情况,添加自定义的一个删除器保证资源释放完全:
templateclass Deleter{public:
void operator()(Ty *ptr)const{cout<<"Call a custom method !!!!! "<std::unique_ptr>ptr(new int[100]);//delete []ptr
return 0;
}
或者使用 default_delete来做删除器
// 可用default_delete来做删除器,default_.delete是标准库里的模板类。
void fun4()
{cout<< "detail5:: func4()"<< endl;
shared_ptrpi5(new int[100](), std::default_delete());
}
改进,争对这种特殊的情况,添加自定义的一个删除器保证资源释放完全:
templateclass Deleter{public:
void operator()(Ty *ptr)const{cout<<"Call a custom method !!!!! "<std::unique_ptr>ptr(new int[100]);//delete []ptr
return 0;
}
或者使用 default_delete来做删除器
// 可用default_delete来做删除器,default_.delete是标准库里的模板类。
void fun4()
{cout<< "detail5:: func4()"<< endl;
shared_ptrpi5(new int[100](), std::default_delete());
}
你是否还在寻找稳定的海外服务器提供商?创新互联www.cdcxhl.cn海外机房具备T级流量清洗系统配攻击溯源,准确流量调度确保服务器高可用性,企业级服务器适合批量采购,新人活动首月15元起,快前往官网查看详情吧
网站栏目:仿函数C++-创新互联
新闻来源:http://scyanting.com/article/cdiece.html