当前位置:   article > 正文

从零开始学C++之STL(八):函数对象、 函数对象与容器、函数对象与算法

从零开始学c++之stl(八):

一、函数对象

1、函数对象(function object)也称为仿函数(functor)


2、一个行为类似函数的对象,它可以没有参数,也可以带有若干参数。


3、任何重载了调用运算符operator()的类的对象都满足函数对象的特征


4、函数对象可以把它称之为smart function。


5、STL中也定义了一些标准的函数对象,如果以功能划分,可以分为算术运算、关系运算、逻辑运算三大类。为了调用这些标准函数对象,需要包含头文件<functional>。


二、自定义函数对象

C++ Code
<nobr>1<br> 2<br> 3<br> 4<br> 5<br> 6<br> 7<br> 8<br> 9<br> 10<br> 11<br> 12<br> 13<br> 14<br> 15<br> 16<br> 17<br></nobr>
#include<iostream>
using namespacestd;
classCFunObj
{
public:
void operator()()
{
cout<< "hello,functionobject!"<<endl;
}
};
intmain()
{
CFunObjfo;
fo();
CFunObj()();
return 0;
}

注意:CFunObj()(); 表示先构造一个匿名对象,再调用operator();


三、函数对象与容器


在这边举map 容器的例子,大家都知道map 在插入元素的时候会自动排序,默认是根据key 从小到大排序,看map 的定义:

C++ Code
<nobr>1<br> 2<br> 3<br> 4<br> 5<br> 6<br> 7<br> 8<br> 9<br> 10<br></nobr>
//TEMPLATECLASSmap
template< class_Kty,
class_Ty,
class_Pr=less<_Kty>,
class_Alloc=allocator<pair< const_Kty,_Ty>>>
classmap
: public_Tree<_Tmap_traits<_Kty,_Ty,_Pr,_Alloc, false>>
{
//orderedred-blacktreeof{key,mapped}values,uniquekeys
};

假设现在我们这样使用 map< int, string > mapTest; 那么默认的第三个参数 _Pr = less<int>,再者,map 继承的其中一个类


_Tmap_traits 中有个成员:


_Pr comp;// the comparator predicate for keys


跟踪进insert 函数,其中有这样一句:


if (_DEBUG_LT_PRED(this->comp,_Key(_Where._Mynode()), this->_Kfn(_Val)))


已知#define _DEBUG_LT_PRED(pred, x, y) pred(x, y) 很明显地,comp 在这里当作函数对象使用,传入两个参数,回头看less 类的


模板实现:

C++ Code
<nobr>1<br> 2<br> 3<br> 4<br> 5<br> 6<br> 7<br> 8<br> 9<br> 10<br> 11<br> 12<br> 13<br></nobr>
//TEMPLATESTRUCTless
template< class_Ty>
structless
: publicbinary_function<_Ty,_Ty, bool>
{
//functorforoperator<
bool operator()( const_Ty&_Left, const_Ty&_Right) const
{
//applyoperator<tooperands
return(_Left<_Right);
}
};

即实现了operator() 函数,左操作数小于右操作数时返回为真。


我们也可以在定义的时候传递第三个参数,如map< int, string, greater<int> > mapTest; 则插入时按key 值从大到小排序(less,


greater 都是STL内置的类,里面实现了operator() 函数),甚至也可以自己实现一个类传递进去,如下面例程所示:

C++ Code
<nobr>1<br> 2<br> 3<br> 4<br> 5<br> 6<br> 7<br> 8<br> 9<br> 10<br> 11<br> 12<br> 13<br> 14<br> 15<br> 16<br> 17<br> 18<br> 19<br> 20<br> 21<br> 22<br> 23<br> 24<br> 25<br> 26<br> 27<br> 28<br> 29<br></nobr>
#include<map>
#include<string>
#include<iostream>

using namespacestd;

structMyGreater
{
bool operator()( intleft, intright)
{
returnleft>right;
}
};

intmain( void)
{
map< int,string, /*greater<int>*/MyGreater>mapTest;
mapTest.insert(map< int,string>::value_type( 1, "aaaa"));
mapTest.insert(map< int,string>::value_type( 3, "cccc"));
mapTest.insert(map< int,string>::value_type( 2, "bbbb"));


for(map< int,string, /*greater<int>*/MyGreater>::const_iteratorit=mapTest.begin();it!=mapTest.end();++it)
{
cout<<it->first<< ""<<it->second<<endl;
}

return 0;
}

输出为:

3 cccc

2 bbbb

1 aaaa


MyGreater 类并不是以模板实现,只是比较key 值为int 类型的大小。


四、函数对象与算法

在STL一些算法中可以传入函数指针,实现自定义比较逻辑或者计算,同样地这些函数也可以使用函数对象来代替,直接看例程再稍

作分析:

C++ Code
<nobr>1<br> 2<br> 3<br> 4<br> 5<br> 6<br> 7<br> 8<br> 9<br> 10<br> 11<br> 12<br> 13<br> 14<br> 15<br> 16<br> 17<br> 18<br> 19<br> 20<br> 21<br> 22<br> 23<br> 24<br> 25<br> 26<br> 27<br> 28<br> 29<br> 30<br> 31<br> 32<br> 33<br> 34<br> 35<br> 36<br> 37<br> 38<br> 39<br> 40<br> 41<br> 42<br> 43<br> 44<br> 45<br> 46<br> 47<br> 48<br> 49<br> 50<br> 51<br> 52<br> 53<br> 54<br> 55<br> 56<br> 57<br> 58<br> 59<br> 60<br> 61<br> 62<br> 63<br> 64<br> 65<br> 66<br> 67<br> 68<br> 69<br> 70<br> 71<br> 72<br> 73<br> 74<br> 75<br> 76<br> 77<br> 78<br> 79<br> 80<br> 81<br> 82<br></nobr>
#include<vector>
#include<string>
#include<iostream>
#include<algorithm>

using namespacestd;

voidPrintFun( intn)
{
cout<<n<< '';
}

voidAdd3( int&n)
{
n+= 3;
}

classPrintObj
{
public:
void operator()( intn)
{
cout<<n<< '';
}
};

classAddObj
{
public:
AddObj( intnumber):number_(number)
{

}
void operator()( int&n)
{
n+=number_;
}

private:
intnumber_;
};

classGreaterObj
{
public:
GreaterObj( intnumber):number_(number)
{

}
bool operator()( intn)
{
returnn>number_;
}
private:
intnumber_;
};


intmain( void)
{
inta[]={ 1, 2, 3, 4, 5};
vector< int>v(a,a+ 5);

/*for_each(v.begin(),v.end(),PrintFun);
cout<<endl;*/


for_each(v.begin(),v.end(),PrintObj());
cout<<endl;

/*for_each(v.begin(),v.end(),Add3);
for_each(v.begin(),v.end(),PrintFun);
cout<<endl;*/


for_each(v.begin(),v.end(),AddObj( 5));
for_each(v.begin(),v.end(),PrintFun);
cout<<endl;


cout<<count_if(a,a+ 5,GreaterObj( 3))<<endl; //计算大于3的元素个数

return 0;
}

输出为:

1 2 3 4 5

6 7 8 9 10

2


回顾for_each 的源码,其中有这样一句:_Func(*_ChkFirst); 也就是将遍历得到的元素当作参数传入函数。


上面程序使用了函数对象,实际上可以这样理解PrintObj()(*_ChkFirst); 即PrintObj() 是一个匿名的函数对象,传入参


数,调用了operator() 函数进行打印输出。使用函数对象的好处是比较灵活,比如直接使用函数Add3,那么只能将元素加3,而


使用函数对象Addobj(x), 想让元素加上多少就传递给Addobj类,构造一个对象即可,因为它可以保存一种状态(类成员)。


count_if 中的 GreaterObj(3) 就类似了,将遍历的元素当作参数传递给operator(), 即若元素比3大则返回为真。



五、STL内置的函数对象类



参考:

C++ primer 第四版
Effective C++ 3rd
C++编程规范



声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/weixin_40725706/article/detail/589677
推荐阅读
相关标签
  

闽ICP备14008679号