飞道的博客

C++(STL源码):37---仿函数(函数对象)源码剖析

393人阅读  评论(0)

一、仿函数概述

  • 仿函数(functors)是早期的命名,C++标准规则定案后所采用的的新名称是函数对象(function objects)
  • 仿函数的作用是什么?从前面的算法解析可以看出,有的算法提供第二个版本,该版本提供允许用户指定任何“操作”,然后以该操作来决定算法的执行功能。将这种“操作”当做算法的参数,先将该操作设计为一个函数,再将函数指针当做算法的一个参数或者将该“操作”设计为一个所谓的仿函数(就语言层面来说是个class),再以该仿函数产生一个对象,并以此对象作为算法的一个参数
  • 上面提到,既然函数指针可以达到“将函数当做算法的参数”,那又为什么设计仿函数呢?是因为函数指针不能满足STL对抽象性的要求,也不能满足软件积木的要求——函数指针无法和STL其他组件(如配接器adapter)搭配,产生更灵活的变化

仿函数就是一个“行为类似函数”的对象

  • 为了能够达到“行为类似函数”的目的,其类型定义中必须自定义(或者说重载、改写)function call运算子(operator())
  • 拥有这样的运算子后,我们就可以在仿函数的对象后面加上一对小括号,以此来调用仿函数所定义的operator()
  • 例如下面就是调用STL提供的greater仿函数:
    • 第一种用法是产生一个名为id的对象,然后调用其operator()
    • 第二种调用方式是产生一个临时(无名的)对象,然后调用其operator()。这种方式才是仿函数的主流用法

   
  1. #include <iostream>
  2. #include <functional>
  3. using namespace std;
  4. int main()
  5. {
  6. greater< int> ig;
  7. std:: cout << boolalpha << ig( 4, 6) << std:: endl;
  8. std:: cout << greater< int>()( 6, 4) << std:: endl;
  9. return 0;
  10. }
  11. //boolalpha是把bool值显示为true或false

 

仿函数的分类

  • 以操作符划分:分为一元仿函数与二元仿函数(没有其他的了)
  • 以功能划分:分为算术运算、关系运算、逻辑运算
  • 应用层头文件为:<functional>;STL源码实现于<stl_function.h>

二、可配接的关键

  • 在STL六大组件中,仿函数是体积最小、实现最容易的一种。而仿函数扮演着一种“策略”角色:
    • ①可以让STL算法有更灵活的演出
    • ②更加灵活的关键在于STL仿函数的可配接性
  • 仿函数可以让函数配接器(function adapter,见后面介绍)修饰,彼此串接在一起。为了拥有配接能力,每一个仿函数必须定义自己的相应类型:
    • 这些类型是为了让配接器能够取出,获得仿函数的某些信息。相应的类型都只是一些typedef,所有必要操作在编译器就能全部确定
    • 仿函数的相应类型主要用来表现仿函数参数类型和返回值类型
    • 这就类似于迭代器如果想要融入STL,迭代器定义了自己的5个相应类型
  • 为了方便起见,<stl_function.h>中定义了两个类,分别代表一元仿函数和二元仿函数,其中没有任何data members或member functions,唯有一些类型定义
  • 对于仿函数,只要根据个人需要选择继承其中一个就可以了,便自动拥有了那些相应类型,也就自动有用了配接能力

unary_function

  • unary_function用来表现一元函数的参数类型和返回值类型
  • 其定义如下:

   
  1. //STL规定,每一个Adaptable Unary Function都应该继承这个类
  2. template < class Arg, class Result>
  3. struct unary_function {
  4. typedef Arg argument_type; //参数类型
  5. typedef Result result_type; //返回值类型
  6. };
  • 一旦某个仿函数继承了unary_function,其用户就可以取得该仿函数的参数类型,或其返回值类型(下面未显示):

   
  1. //此仿函数继承于unary_function
  2. template< class T>
  3. struct negate : public unary_function<T, T>{
  4. T operator()(const T& x)const { return -x; }
  5. };
  6. //以下配接器用来表示某个仿函数的逻辑负值
  7. template< class Predicate>
  8. class unary_negate
  9. {
  10. //...
  11. public:
  12. //通过typename获得其中的参数类型
  13. bool operator()(const typename Predicate::argument_type& x)const {
  14. //...
  15. }
  16. //...
  17. };
  • 在后面介绍仿函数配接器的源码时可以见到

binary_function

  • binary_function用来表现二元函数的参数类型和返回值类型
  • 其定义如下::

   
  1. //STL规定,每一个Adaptable Binary Function都应该继承这个类
  2. template < class Arg1, class Arg2, class Result>
  3. struct binary_function {
  4. typedef Arg1 first_argument_type; //第1个参数的类型
  5. typedef Arg2 second_argument_type; //第2个参数的类型
  6. typedef Result result_type; //返回值类型
  7. };
  • 一旦某个仿函数继承了binary_function,其用户就可以取得该仿函数的参数类型,或其返回值类型(下面未显示):

   
  1. //此仿函数继承于binary_function
  2. template< class T>
  3. struct plus : public binary_function<T, T, T>{
  4. T operator()(const T& x, const T& y)const { return x + y; }
  5. };
  6. //以下配接器用来将某个二元仿函数转换为一元仿函数
  7. template< class Operation>
  8. class binder1st
  9. {
  10. //...
  11. protected:
  12. Operation op;
  13. typename Operation::fist_argument_type value; //将其参数1类型,别名为value
  14. public:
  15. //下面是operator()的定义
  16. typename Operation:: result_type
  17. operator () (const typename Operation::second_argument_type& x)
  18. {
  19. //...
  20. }
  21. //...
  22. };
  • 在后面介绍仿函数配接器的源码时可以见到

四、算术类(Arithmetic)仿函数

  • STL内建的“算术类仿函数”,支持加法、减法、乘法、除法、模数(余数)、和否定运算
  • 除了“否定”运算为一元运算,其余都是二元运算
  • 包含如下:
    • 加法:plus<T>
    • 减法:minus<T>
    • 乘法:multiplies<T>
    • 除法:divides<T>
    • 模取(modulus):modulus<T>
    • 否定(negation):negate<T>
  • 例如下面的代码表示要以1位基本元素,对vector中的每一个元素进行乘法运算:
accumulate(iv.begin(), iv.end(), multiplies<int>());

源码如下:


   
  1. template < class T>
  2. struct plus : public binary_function<T, T, T> {
  3. T operator()(const T& x, const T& y)const { return x + y; }
  4. };
  5. template < class T>
  6. struct minus : public binary_function<T, T, T> {
  7. T operator()(const T& x, const T& y)const { return x - y; }
  8. };
  9. template < class T>
  10. struct multiplies : public binary_function<T, T, T> {
  11. T operator()(const T& x, const T& y)const { return x * y; }
  12. };
  13. template < class T>
  14. struct divides : public binary_function<T, T, T> {
  15. T operator()(const T& x, const T& y)const { return x / y; }
  16. };
  17. template < class T>
  18. struct modulus : public binary_function<T, T, T> {
  19. T operator()(const T& x, const T& y)const { return x % y; }
  20. };
  21. template < class T>
  22. struct negate : public unary_function<T, T> {
  23. T operator()(const T& x)const { return -x; }
  24. };

演示案例


   
  1. #include <iostream>
  2. #include <functional>
  3. using namespace std;
  4. int main()
  5. {
  6. plus< int> plusobj;
  7. minus< int> minusobj;
  8. multiplies< int> multipliesobj;
  9. divides< int> dividesobj;
  10. modulus< int> modulusobj;
  11. negate< int> negateobj;
  12. std:: cout << plusobj( 3, 5) << std:: endl;
  13. std:: cout << minusobj( 3, 5) << std:: endl;
  14. std:: cout << multipliesobj( 3, 5) << std:: endl;
  15. std:: cout << dividesobj( 3, 5) << std:: endl;
  16. std:: cout << modulusobj( 3, 5) << std:: endl;
  17. std:: cout << negateobj( 3) << std:: endl;
  18. std:: cout << "**************************" << std:: endl;
  19. std:: cout << plus< int>()( 3, 5) << std:: endl;
  20. std:: cout << minus< int>()( 3, 5) << std:: endl;
  21. std:: cout << multiplies< int>()( 3, 5) << std:: endl;
  22. std:: cout << divides< int>()( 3, 5) << std:: endl;
  23. std:: cout << modulus< int>()( 3, 5) << std:: endl;
  24. std:: cout << negate< int>()( 3) << std:: endl;
  25. return 0;
  26. }

 

证同元素(identity element)

  • 所谓“运算op的证同元素”,意思是说数值A若与该元素做op运算,会得到A自己
  • 例如:加法的证同元素为0(因为任何元素加上0仍为自己)。乘法的证同元素为1(任何元素乘以1仍为自己)
  • 下面这些函数并非STL标准中的一员,但很多STL都实现了它们:

五、关系运算类(Relational)仿函数

  • STL内建的“关系运算类仿函数”,支持等于、不等于、大于、大于等于、小于、小于等于六种运算
  • 每一个都是二元运算
  • 包含如下:
    • 等于(equality):equal_to<T>
    • 不等于(inequality):not_equal_to<T>
    • 大于(greater than):greater<T>
    • 大于或等于(greater than or equal):greater_equal<T>
    • 小于(less than):less<T>
    • 小于或等于(less than or equal):less_equal<T>
  • 例如下面的代码表示以递增次序对vector进行排序:
sort(iv.begin(), iv.end(), greater<int>());

源码如下:


   
  1. template< class T>
  2. struct equal_to : public binary_function<T, T, bool> {
  3. bool operator()(const T& x, const T& y)const { return x == y; }
  4. };
  5. template< class T>
  6. struct not_equal_to : public binary_function<T, T, bool> {
  7. bool operator()(const T& x, const T& y)const { return x != y; }
  8. };
  9. template< class T>
  10. struct greater : public binary_function<T, T, bool> {
  11. bool operator()(const T& x, const T& y)const { return x > y; }
  12. };
  13. template< class T>
  14. struct less : public binary_function<T, T, bool> {
  15. bool operator()(const T& x, const T& y)const { return x < y; }
  16. };
  17. template< class T>
  18. struct greater_equal : public binary_function<T, T, bool> {
  19. bool operator()(const T& x, const T& y)const { return x >= y; }
  20. };
  21. template< class T>
  22. struct less_equal : public binary_function<T, T, bool> {
  23. bool operator()(const T& x, const T& y)const { return x <= y; }
  24. };

演示案例


   
  1. #include <iostream>
  2. #include <functional>
  3. using namespace std;
  4. int main()
  5. {
  6. equal_to< int> equal_to_obj;
  7. not_equal_to< int> not_equal_to_obj;
  8. greater< int> greater_obj;
  9. greater_equal< int> greater_equal_obj;
  10. less< int> less_obj;
  11. less_equal< int> less_equal_obj;
  12. std:: cout << equal_to_obj( 3, 5) << std:: endl;
  13. std:: cout << not_equal_to_obj( 3, 5) << std:: endl;
  14. std:: cout << greater_obj( 3, 5) << std:: endl;
  15. std:: cout << greater_equal_obj( 3, 5) << std:: endl;
  16. std:: cout << less_obj( 3, 5) << std:: endl;
  17. std:: cout << less_equal_obj( 3, 5) << std:: endl;
  18. std:: cout << "**************************" << std:: endl;
  19. std:: cout << equal_to< int>()( 3, 5) << std:: endl;
  20. std:: cout << not_equal_to< int>()( 3, 5) << std:: endl;
  21. std:: cout << greater< int>()( 3, 5) << std:: endl;
  22. std:: cout << greater_equal< int>()( 3, 5) << std:: endl;
  23. std:: cout << less< int>()( 3, 5) << std:: endl;
  24. std:: cout << less_equal< int>()( 3, 5) << std:: endl;
  25. return 0;
  26. }

六、逻辑运算类(Logical)仿函数

  • STL内建的“逻辑运算类仿函数”,支持逻辑运算中的And、Or、Not三种运算
  • 其中And和Or为二元运算,Not为一元运算
  • 包含如下:
    • 逻辑运算 And:logical_and<T>
    • 逻辑运算Or:logical_or<T>
    • 逻辑运算 Not:logical_not<T>

源码如下:


   
  1. template< class T>
  2. struct logical_and : public binary_function<T, T, bool> {
  3. bool operator()(const T& x, const T& y)const { return x&&y; }
  4. };
  5. template< class T>
  6. struct logical_or : public binary_function<T, T, bool> {
  7. bool operator()(const T& x, const T& y)const { return x||y; }
  8. };
  9. template< class T>
  10. struct logical_not : public binary_function<T, T, bool> {
  11. bool operator()(const T& x, const T& y)const { return x!=y; }
  12. };

演示案例


   
  1. #include <iostream>
  2. #include <functional>
  3. using namespace std;
  4. int main()
  5. {
  6. logical_and< int> and_obj;
  7. logical_or< int> or_obj;
  8. logical_not< int> not_obj;
  9. std:: cout << and_obj( true, true) << std:: endl;
  10. std:: cout << or_obj( true, true) << std:: endl;
  11. std:: cout << not_obj( true) << std:: endl;
  12. std:: cout << "**************************" << std:: endl;
  13. std:: cout << logical_and< int>()( true, true) << std:: endl;
  14. std:: cout << logical_or< int>()( true, true) << std:: endl;
  15. std:: cout << logical_not< int>()( true) << std:: endl;
  16. return 0;
  17. }

七、证同(identity)、选择(select)、投射(project)

  • 下面介绍的这些仿函数,都只是将其参数原封不动地传回。其中某些仿函数对传回的参数有刻意的选择,或者可以的忽略
  • 之所以不在STL或其他泛型程序中直接使用原本及其简单的identity、project、select等操作,而要再划分一层出来,全是为了间接性——间接性是抽象化的重要工具
  • C++标准并为包含下面的这几个仿函数,不过它们常常存在于各个实现品作为内部使用。下面是SGI STL的版本

identity

select1st

select2nd

project1st

project2nd


转载:https://blog.csdn.net/qq_41453285/article/details/104303680
查看评论
* 以上用户言论只代表其个人观点,不代表本网站的观点或立场