转载声明:本文转自网络,稍加整理以备学习和參考之用。

函数对象/仿函数

提到C++ STL。首先被人想到的是它的三大组件:Containers, Iterators, Algorithms,即容器,迭代器和算法。容器为用户提供了经常使用的数据结构,算法大多是独立于容器的经常使用的基本算法,迭代器是由容器提供的一种接口。算法通过迭代器来操控容器。接下来要介绍的是另外的一种组件。函数对象(Function
Object,JJHou译作Functor仿函数)。

什么是函数对象

  顾名思义,函数对象首先是一个对象,即某个类的实例。

其次,函数对象的行为和函数一致,即是说能够像调用函数一样来使用函数对象,如參数传递、返回值等。

这样的行为是通过重载类的()操作符来实现的,

仿函数:用途和适用的场合

  之所以要开发仿函数(functors),是由于函数不能容纳不论什么有意义的状态。

比如。使用函数,你不能为某个元素加一个随意值。再将其应用于一个范围。可是,使用仿函数可轻易做到这一点。

举个栗子。。。



#include <iostream>
#include <algorithm>
using namespace std;
//回调函数
void call_back(char elem)
{
 cout << elem << endl;
}
//仿函数
struct Functor
{
 void operator() (char elem) 
 {
  cout << elem << endl;
 
};
int main()
{
 string strA = "hello";
 string strB = "world";
  
 for_each(strA.begin(),strA.end(),Functor());
 cout<<"===========GAP==============="<<endl;
 for_each(strB.begin(),strB.end(),call_back);
 getchar();
 return 0;
}

h

e

l

l

o

===========GAP===============

w

o

r

l

d

可能会有疑问两者有什么差别?

假如我要for_each遍历的不是字符串而是int类型的vector呢?

是不是又要重写一个int类型作为參数的回调函数,那假设有N种类型的容器遍历岂不是要写N个回调函数或N个仿函数类?

非也!!!

C++有类模板 也有 函数模板 相同能够用于回调

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;
//模板函数
template<typename T>
void call_back(T elem)
 cout<< elem <<endl;
}
//仿函数
template<typename T>
class Functor
{
public:
 Functor()
  :m_val(0)
 {
  cout<< "Functor()" <<endl;
 }
 ~Functor()
 {
  cout<<"~Functor()"<<endl;
 }
 void operator() (T elem) 
 {
  Do(elem);
 }
  
 //举个栗子
 void Do(T elem)
 {
  m_val+=elem;
  cout<<elem<<"/"<<m_val<<endl;
 }
private:
 T m_val;
};
  
int main()
{
 vector<int> vec;
 vec.push_back(1);
 vec.push_back(2);
 vec.push_back(3);
 vec.push_back(4);
 vec.push_back(5);
 for_each(vec.begin(),vec.end(),call_back<int>);
 cout<<"===========GAP==============="<<endl; 
 for_each(vec.begin(),vec.end(),Functor<int>());
 return 0;
}

1

2

3

4

5

===========GAP===============

Functor()

1/1

2/3

3/6

4/10

5/15

~Functor()

~Functor()

~Functor()

三次析构的原因:

先附上for_each的源代码(VC2008)

1
2
3
4
5
6
7
8
9
10
11
template<class _InIt,class _Fn1>
inline _Fn1 for_each(_InIt _First, _InIt _Last, _Fn1 _Func)
// perform function for each element
 _DEBUG_RANGE(_First, _Last);
 _DEBUG_POINTER(_Func);
 _CHECKED_BASE_TYPE(_InIt) _ChkFirst(_CHECKED_BASE(_First));
 _CHECKED_BASE_TYPE(_InIt) _ChkLast(_CHECKED_BASE(_Last));
 for (; _ChkFirst != _ChkLast; ++_ChkFirst)
  _Func(*_ChkFirst);
 return (_Func);
}
?
1
2
3
4
5
Functor<int>() 产生暂时对象传參(值) 构造一次,析构一次 
for_each參数值传递,拷贝构造一次,析构一次(函数内部)
for_each返回仿函数的对象(值),拷贝构造一次,析构一次
由于没有重载拷贝构造函数 所以打印出第一次创建暂时对象时的普通构造函数
实际上在这个过程中一共产生过三个仿函数对象

假设把代码改变下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;
 
template<typename T>
class Functor
{
public:
 Functor()
  :m_val(0)
 {
  cout<< "Functor()"<<this<<endl;
 }
 Functor(Functor& that)
 {
  this->m_val = that.m_val;
  cout<< "Copy Functor()" <<this<<endl;
 }
 ~Functor()
 {
  cout<<"~Functor()"<<this<<endl;
 }
 void operator() (T elem) 
 {
  Do(elem);
 }
  
 //举个栗子
 void Do(T elem)
 {
  m_val+=elem;
  cout<<elem<<"/"<<m_val<<endl;
 }
 T getVal()
 {
  return m_val;
 }
private:
 T m_val;
};
  
int main()
{
 vector<int> vec;
 vec.push_back(1);
 vec.push_back(2);
 vec.push_back(3);
 vec.push_back(4);
 vec.push_back(5);
 
 Functor<int> func;
 Functor<int>& ref = for_each(vec.begin(),vec.end(),func);
 cout<<ref.getVal()<<endl;
  
 return 0;
}

执行结果

Functor()0032F800           //main函数中的实參仿函数对象

Copy Functor()0032F68C  //值传递 对【实參对象】拷贝构造了形參对象

1/1

2/3

3/6

4/10

5/15

Copy Functor()0032F7E8 //返回对象的值类型  对【形參对象】拷贝构造

~Functor()0032F68C       //析构形參对象

15

~Functor()0032F7E8       //析构返回值对象

~Functor()0032F800      //析构实參对象

如今一目了然了吧!

使用回调函数高效 由上面的样例能够看出 构造1次 拷贝构造2次 析构3次  是有代价的

最后回到仿函数和回调函数

差别在于:

  1. 使用仿函数能够声明在业务相关的类内部 缩小作用域

  2. 使用仿函数能够使用类的成员属性和成员函数

  3. 仿函数是一个类 能够使用面向对象的各种机制(封装
    继承 多态)

  4. 若使用回调函数 那么仅仅能声明为某个类的静态成员函数或全局函数。使用类内部的资源须要用一些手段传參,没有直接使用成员函数便捷

Functor仿函数的更多相关文章

  1. 仿函数(二、stl中常用仿函数)

    提到C++ STL,首先被人想到的是它的三大组件:Containers, Iterators, Algorithms,即容器,迭代器和算法.容器为用户提供了常用的数据结构,算法大多是独立于容器的常用的 ...

  2. js Functor Copy

    原文地址:https://segmentfault.com/a/1190000006051586?utm_source=tuicool&utm_medium=referral 本处仅仅个人存档 ...

  3. Java基础常见英语词汇

    Java基础常见英语词汇(共70个) ['ɔbdʒekt] ['ɔ:rientid]导向的                             ['prəʊɡræmɪŋ]编程 OO: object ...

  4. IT软件开发常用英语词汇

    Aabstract 抽象的abstract base class (ABC)抽象基类abstract class 抽象类abstraction 抽象.抽象物.抽象性access 存取.访问access ...

  5. computer English

    算法常用术语中英对照Data Structures 基本数据结构Dictionaries 字典PriorityQueues 堆Graph Data Structures 图Set Data Struc ...

  6. IT软件开发中常用的英语词汇

    Aabstract 抽象的abstract base class (ABC)抽象基类abstract class 抽象类abstraction 抽象.抽象物.抽象性access 存取.访问access ...

  7. 整理C++面试题for非CS程序猿——更新至【48】

    结合网上的C++面试题+自己的面经,进行整理记录,for我这种非CS的程序猿.(不定期更新,加入了自己的理解,如有不对,请指出) [1] new/delete和malloc/free的区别和联系? 1 ...

  8. 看到了必须要Mark啊,最全的编程中英文词汇对照汇总(里面有好几个版本的,每个版本从a到d的顺序排列)

    java:  第一章: JDK(Java Development Kit) java开发工具包 JVM(Java Virtual Machine) java虚拟机 Javac  编译命令 java   ...

  9. php英语单词大全95

    abstract抽象的 -挨伯丝拽克特 access存取.访问 -挨克色丝 account账户 -厄靠恩特 action动作 -爱克身 activate激活 -爱克特维特 active活动的 -爱克得 ...

随机推荐

  1. Android JNI programming demo with Eclipse

    用Eclipse 建立 JNI 的專案, 示範怎样在 JAVA 調用 cpp 的函數. 我們將建立一個名稱為 jnidemo的專案, 在主Activity 將調用一個名為libHello.so 的 c ...

  2. WPF的消息机制

    前言 谈起“消息机制”这个词,我们都会想到Windows的消息机制,系统将键盘鼠标的行为包装成一个Windows Message,然后系统主动将这些Windows Message派发给特定的窗口,实际 ...

  3. POJ 1466 最大独立集入门

    题意:n个学生,给你每个学生浪漫的学生学号(男女之间浪漫),问你找出一个最大的集合保证集合内的任意两个学生之间没有相互浪漫关系,输出最大集合的人数. 注意:这里的浪漫边是双向的,如果1对2浪漫, 那么 ...

  4. Ext JS4百强应用:设置textfield的悬浮提示信息 --第8强

    在Extjs4中有时候我们需要textfield的提示信息,但是我们发现textfield并没有这样的配置项. 这时候我们就要另想方法:我们需要在鼠标悬停在textfield组件的时候进行信息的提示, ...

  5. ios23- 文件下载(同步和异步)

    1.第一步:创建一个单例视图 #import <UIKit/UIKit.h> @interface ios23_downViewController : UIViewController& ...

  6. Linux下套接字具体解释(三)----几种套接字I/O模型

    參考: 网络编程–IO模型演示样例 几种server端IO模型的简介及实现 背景知识 堵塞和非堵塞 对于一个套接字的 I/O通信,它会涉及到两个系统对象.一个是调用这个IO的进程或者线程,还有一个就是 ...

  7. SQLServer行转列

    近期面试遇到了一道面试题.顿时有点迷糊,仅仅说出了思路.后来百度了一下.整理了一下思路,于是记录下来,方便以后学习.(面试题请參见附件) 相关的数据表: 1.Score表 2.[User]表 SQL语 ...

  8. Windows Azure使用体验

    Windows Azure在今年6月6日由世纪互联代理在中国运营,目前只能体验,没有开放注册.不过,体验的门槛比较高,只对企业开放,未来大量对外开放使用貌似时间还早.大家都懂得,“国内门槛高”.本人在 ...

  9. windows下RabbitMQ 监控

    RabbitMQ的监控很简单,网上也有很多资料,但是大都不详细,让人云里雾里,我这里详细总结下. RabbitMQ本身提供了一个web的监控页面,只需要简单的几部命令行就可以访问这个页面了. 1.打开 ...

  10. 安装logstash,elasticsearch,kibana三件套(转)

    logstash,elasticsearch,kibana三件套 elk是指logstash,elasticsearch,kibana三件套,这三件套可以组成日志分析和监控工具 注意: 关于安装文档, ...