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

函数对象/仿函数

提到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. MSSQL - 根据时间倒序删除第一行数据

    delete top(1) from Tb_PaintOut where PaintNumber = (select top (1) PaintNumber from Tb_PaintOut orde ...

  2. 删除PO

    FUNCTION zrfc_mm002. *"---------------------------------------------------------------------- * ...

  3. MySQL数据库触发器(trigger)

    MySQL触发器(trigger):监视某种情况并触发某种操作 一:四要素 触发时间:before/after 地点:table 监视操作:insert/update/delete 触发操作:inse ...

  4. POJ 2115 C Looooops(扩展欧几里得应用)

    题目地址:POJ 2115 水题. . 公式非常好推.最直接的公式就是a+n*c==b+m*2^k.然后能够变形为模线性方程的样子,就是 n*c+m*2^k==b-a.即求n*c==(b-a)mod( ...

  5. JSP自定义标签——简单标签(2)

    在前一篇博客中,我们已经学习了自定义的简单标签的基本使用方法,这一篇我们来学习如何在简单标签中添加标签属性.对自定义标签添加一些属性,可以使我们的标签功能更加灵活和复用.例如前一篇博客使用简单标签来对 ...

  6. javascript中apply和eval结合的强大用法

        eval是一个函数,可以接受一个参数,这个参数可以作为js语句被解释性的执行,利用这个特性,eval和apply结合起来,可以大大简化代码  如下例子 <a class="cl ...

  7. c语言指针具体解释

    指针是C语言中广泛使用的一种数据类型. 运用指针编程是C语言最基本的风格之中的一个.利用指针变量能够表示各种数据结构: 能非常方便地使用数组和字符串: 并能象汇编语言一样处理内存地址,从而编出精练而高 ...

  8. String[255]在高版本Delphi里还是被解释成Byte,总体长度256,使用StrPCopy可以给Array String拷贝字符串(内含许多实验测试)

    学了好多不了解的知识: procedure TForm1.Button1Click(Sender: TObject); var s1 : String; s2 : String[]; begin s1 ...

  9. ActiveX控件的安全初始化和脚本操作 和 数字签名SIGN

    摘要:数字签名SIGN保证控件在下载时候的安全性.如果你的代码已经经过数字签名,即使用户IE的安全设置很高也能下载,安装并登记.但是在页面上初始化,或者用脚本运行这个控件,为了保证安全性,还需要进行M ...

  10. 编程语言性能游戏排行榜,C/C++第一ATS第二JAVA第三

    编程语言性能游戏排行榜,C/C++第一ATS第二JAVA第三 编程语言性能游戏排行榜,C/C++第一ATS第二JAVA第三