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

函数对象/仿函数

提到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. iOS- 如何集成支付宝

    链接地址:http://www.cnblogs.com/qingche/p/3556365.html 现在不少app内都集成了支付宝功能   使用支付宝进行一个完整的支付功能,大致有以下步骤: 1&g ...

  2. Python爬虫入门三之Urllib库的基本使用

    转自http://cuiqingcai.com/947.html 1.分分钟扒一个网页下来 怎样扒网页呢?其实就是根据URL来获取它的网页信息,虽然我们在浏览器中看到的是一幅幅优美的画面,但是其实是由 ...

  3. net core VS goang web

    asp.net core VS goang web[修正篇] 先前写过一篇文章:http://www.cnblogs.com/gengzhe/p/5557789.html,也是asp.net core ...

  4. 图解C#_事件

    概述 今天用来演示事件的例子是模拟实现一个文件下载类,在这个类中我将定义一个DownLoad事件,这个事件用来在文件下载的过程中,向订阅这个事件的用户发出消息,而这个消息将用DownLoadEvent ...

  5. 深入浅出Hadoop实战开发(HDFS实战图片、MapReduce、HBase实战微博、Hive应用)

    Hadoop是什么,为什么要学习Hadoop?     Hadoop是一个分布式系统基础架构,由Apache基金会开发.用户可以在不了解分布式底层细节的情况下,开发分布式程序.充分利用集群的威力高速运 ...

  6. jquery如何在加载完iframe的内容后才进行下一步操作

    为iframe添加onload事件 ie使用attachEvent("onload",function(){}) firefox.chrome使用addEventListener( ...

  7. Swift - 字符串(String)用法详解

    下面对String常用的属性和方法做个总结 1,判断是否为空:isEmpty 1 2 3 var str:String if str.isEmpty{ } 2,获取字符数量:countElements ...

  8. twitter分享问题(四)—— Unknown error(api v1过度到V1.1产生)

    unknow error! 今天为了使用GA(谷歌分析)追踪twitter分享,又测试了一下twitter分享功能,发现又出问题(使用sharekit分享).就是“unknow error”,之前也碰 ...

  9. Servlet的学习之Response响应对象(1)

    在之前学习了Servlet中的主体结构,包括Servlet的生命周期方法,和非生命周期方法能获取的一些非常重要的对象如ServletConfig.ServletContext对象等,而从这篇开始我们将 ...

  10. metasploit学习之ms03_026

    傻瓜式利用ms03_026_dcom: Matching Modules ================ Name Disclosure Date Rank Description ---- --- ...