函数对象:定义了调用操作符()的类对象。当用该对象调用此操作符时,其表现形式如同普通函数调用一般,因此取名叫函数对象。举个最简单的例子:

class A
{
public:  int operator() ( int val )  {  return val > 0 ? val : -val;}
};  

类A中定义了操作符 ( ),A对象调用语句在形式上跟以下函数的调用完全一样:

int i = -1;
A func;
cout << func(i);

与普通函数相比,函数对象比函数更加灵活,函数对象的优势:

  • 函数对象可以有自己的状态。我们可以在类中定义状态变量,这样一个函数对象在多次的调用中可以共享这个状态;
  • 函数对象有自己特有的类型。我们可以传递相应的类型作为参数来实例化相应的模板,比如说带参数的函数形参。

使用函数对象的典型例子:

1、字符串排序规则

在容器set 中对string 进行排序,首先来定义相应的类并定义 () 来规定排序规则:

class Sort
{
public:  bool operator() (const string &str1, const string &str2) const  //带两个参数{  return str1 > str2;  }
};  

然后我们可以用这个类作为参数来初始化set容器:

set<string, Sort> myset;  //带比较函数的set构造函数,并用函数对象Sort初始化
myset.insert("A");
myset.insert("B");  

这样容器内容输出为:B,A。

2、谓词函数 

谓词函数通常用来对传进来的参数进行判断,并返回布尔值。但是一般的函数形式固化,比如字符串长度比较只能判断是否大于一个确定的长度值。函数对象可以作为谓词函数,并可以在类初始化时传递参数,如字符串长度参考值,因此函数对象比普通函数更加灵活。

现在假设我们有一串数字,要从中找出第一个不小于10的数字。可以定义如下相应的类:

class Upper
{
public:  Upper(int min = 0):m_min(min){}  bool operator() (int value) const  {  return value >= m_min;  }
private:  int m_min;
};  

从而这样调用 find_if 函数:

find_if( dest.begin(), dest.end(), Upper(10) );

首先生成类 Upper 的对象,并用 10 初始化,调用find_if 时将用该函数对象进行判断。

请注意:在调用用到函数对象的标准库算法时,除非显式地指定模板类型为传引用,否则默认情况下函数对象是按值传递的!因此,如果传递一个具有内部状态的函数对象,则被改变状态的是函数内部被复制的临时对象,函数结束后随之消失。真正传进来的函数对象状态并为改变。

class B
{
public:  B(int n=0) : th(n),count(1) {}  bool operator() (int)  {  return count++ == th;  }  int getCount() const  {  return count;  }
private:  int th;  int count;
};  

测试如下:

vector<int> vec;
for( int i = 3; i!= 13; ++i )
vec.push_back(i);
B b(3);
vector<int>::iterator iter = find_if( vec.begin(), vec.end(), b ); //调用函数对象,查找第三个数字
cout<< "3rd:" << *iter <<endl;
cout<< "State:" << b.getCount() <<endl;  //指向函数对象内容,但内部值却为改变

输出结果为,确实能找到第三个数字(5),但查看b的状态时,返回的 count 依然为0,说明:

  • 在find_if 函数执行期间,内部状态的临时对象发生改变;
  • 在函数对象调用完成后,临时对象消失,原来的状态保持不变。

原则:

不是所有的返回布尔值的函数对象都适合作为谓词函数,因此用作谓词函数的函数对象,最好不要依赖其内部状态的改变。


标准库定义的函数对象

标准库定义了一组算术、关系与逻辑函数对象类。标准库还定义了一组函数适配器,使我们能够特化或者扩展标准库所定义的以及自定义的函数对象类。这些标准库函数对象类型是在 functional 头文件中定义的。

plus函数对象的应用:

plus<int> intAdd; // intAdd为函数对象
int sum = intAdd(10, 20); // 使用函数对象相加两个操作数
plus<string> stringAdd; //
string strs = stringAdd("ab", "12"); // 使用函数对象相加两个操作数

函数对象的函数适配器

适配器分为如下两类:
1. 绑定器,是一种函数适配器,它通过将一个操作数绑定到给定值而将二元函数对象转换为一元函数对象。
2. 求反器,是一种函数适配器,它将谓词函数对象的真值求反。

绑定器

标准库定义了两个绑定器适配器:bind1st 和 bind2nd。每个绑定器接受一个函数对象和一个值。

  • bind1st 将给定值绑定到二元函数对象的第一个实参;
  • bind2nd 将给定值绑定到二元函数对象的第二个实参。

为了计算一个容器中所有小于或等于 10 的元素的个数,可以这样给 count_if 传递值:

count_if( vec.begin(), vec.end(), bind2nd( less_equal<int>(), 10 ) );

传给 count_if 的第三个实参使用 bind2nd 函数适配器,该适配器返回一个函数对象,该对象用 10 作右操作数应用 <= 操作符。这个 count_if 调用计算输入范围中小于或等于 10 的元素的个数。
求反器

标准库还定义了两个求反器:not1 和 not2。

  • not1 将一元函数对象的真值求反;
  • not2 将二元函数对象的真值求反。

为了对 less_equal 函数对象的绑定求反,可以编写这样的代码:

count_if( vec.begin(), vec.end(),not1( bind2nd(less_equal<int>(), 10 ) ) );

首先将 less_equal 对象的第二个操作数绑定到 10,实际上是将该二元操作转换为一元操作。再用 not1 对操作的返回值求反,效果是测试每个元素是否 <=。然后,对结果真值求反。这个 count_if 调用的效果是对大于 10 的那些元素进行计数。

C++函数对象operator()相关推荐

  1. C++:函数指针进阶(四):函数对象operator()

    C++中的  operator() 有两大主要作用: 1:Overloading --------重载() 操作符 2: Casting------实现对象类型转化 1:Overloading重载() ...

  2. 函数对象、 函数对象与容器、函数对象与算法

    一.函数对象 1.函数对象(function object)也称为仿函数(functor) 2.一个行为类似函数的对象,它可以没有参数,也可以带有若干参数. 3.任何重载了调用运算符operator( ...

  3. C++模板学习之函数对象之谓词

    函数对象是用对象来表示的函数: 可以执行operator()的对象都叫做函数对象. 谓词是那些返回bool,operator()操作的函数对象. 考虑如何对一个序列求和: 函数对象的优势在于可以将参数 ...

  4. Python中的sorted函数以及operator.itemgetter函数

    from:Python中的sorted函数以及operator.itemgetter函数 operator.itemgetter函数 operator模块提供的itemgetter函数用于获取对象的哪 ...

  5. C++中的函数指针和函数对象总结

    篇一.函数指针 函数指针:是指向函数的指针变量,在C编译时,每一个函数都有一个入口地址,那么这个指向这个函数的函数指针便指向这个地址. 函数指针的用途是很大的,主要有两个作用:用作调用函数和做函数的参 ...

  6. C++ Primer 5th笔记(chap 14 重载运算和类型转换)lambda函数对象

    1. 定义 lambda是函数对象:编写一个lambda后,编译器会将该表达式转换成一个未命名类的未命名对象,类中含有一个重载的函数调用运算符. eg. stable_sort(words.begin ...

  7. Boost Part III. 函数对象与高级编程 Library 10. Lambda 用法

    让你的函数对象可以与Boost.Lambda 一起使用 不是所有的表达式都适合使用 lambda 表达式,复杂的表达式更适合使用普通的函数对象,而且会多次重用的表达式也应该成为你代码中的一等公民.它们 ...

  8. STL算法中函数对象和谓词

    算法中函数对象和谓词 函数对象和谓词定义 函数对象 谓词 一元函数对象案例 一元谓词案例 二元函数对象案例 二元谓词案例 预定义函数对象和函数适配器 使用预定义函数对象 算术函数对象 关系函数对象 逻 ...

  9. 【转】回调函数,函数指针与函数对象

    原文出处:http://shudingbo.spaces.live.com/blog/cns!C33400475B08F157!423.entry?wa=wsignin1.0&sa=24651 ...

最新文章

  1. 武汉大学计算机学院 毕业答辩,“云答辩”详细流程出炉! 武大本科生毕业答辩这样办...
  2. centos7 源码编译安装mysql 5.7.21
  3. 关于获取客户端Mac地址
  4. html css 重复,CSS重复定义的问题请教_html/css_WEB-ITnose
  5. AUTOSAR从入门到精通100讲(一)-SPI、UART、I2C总线详解
  6. redis哨兵主从不切换_《「面试突击」—Redis篇》-- Redis的主从复制?哨兵机制?...
  7. 平台(洛谷P1105题题解,Java语言描述)
  8. tcpdump命令--详解
  9. 用python在大麦网抢票视频_大麦网怎么抢票快 大麦网抢票最快的方法
  10. Matlab/Simulink Embedded Coder一个非常迷幻的问题
  11. 手机邮箱怎么弄_手机邮箱设置
  12. 在vm虚拟机上安装esd格式的win7系统文件
  13. Android多语言设置
  14. Compose实战-以MVI的方式写Compose
  15. Caffe(to be continued)
  16. 使用虹软SDK实现离线人脸注册,人脸登录(H5-JS前端,java后台)
  17. AI产品经理面试问题积累
  18. vb.net画图程序介绍
  19. python房屋租赁管理系统设计与实现报告_基于web的房屋出租管理系统的设计与开发.doc...
  20. 最大程序员富豪群破茧

热门文章

  1. 多方收集的Winodws Xp相关技术文章
  2. 在C#中,如何用最装逼的代码和最快的速度拷贝数组?
  3. 2021-01-03自我介绍
  4. php非默认安装的扩展库,PHP安装新的扩展库_PHP教程
  5. 华为nove计算机在哪里,关于华为Nova怎么连接电脑的解答
  6. Linux下卸载RabbitMQ
  7. taro3-不使用-typescript-的情况下使用-taro-ui-时报错
  8. GB28181学习笔记6 解析invite命令
  9. 武侠乂怎么修改服务器,武侠乂常见问题解决方法
  10. sqlplus之 SGA和后台进程