返回顶部
首页 > 资讯 > 后端开发 > 其他教程 >一文读懂C++中的函数对象
  • 609
分享到

一文读懂C++中的函数对象

2023-06-06 18:06:00 609人浏览 薄情痞子
摘要

这篇文章给大家介绍一文读懂c++中的函数对象,内容非常详细,感兴趣的小伙伴们可以参考借鉴,希望对大家能有所帮助。如果一个类将()运算符重载为成员函数,这个类就称为函数对象类,这个类的对象就是函数对象。函数对象是一个对象,但是使用的形式看起来

这篇文章给大家介绍一文读懂c++中的函数对象,内容非常详细,感兴趣的小伙伴们可以参考借鉴,希望对大家能有所帮助。

如果一个类将()运算符重载为成员函数,这个类就称为函数对象类,这个类的对象就是函数对象。函数对象是一个对象,但是使用的形式看起来像函数调用,实际上也执行了函数调用,因而得名。

下面是一个函数对象的例子。

#include <iOStream>using namespace std;class CAverage{public:  double operator()(int a1, int a2, int a3)  { //重载()运算符    return (double)(a1 + a2 + a3) / 3;  }};int main(){  CAverage average; //能够求三个整数平均数的函数对象  cout << average(3, 2, 3); //等价于 cout << average.operator(3, 2, 3);  return 0;}

程序的输出结果是:
2. 66667

()是目数不限的运算符,因此重载为成员函数时,有多少个参数都可以。

average 是一个对象,average(3, 2, 3) 实际上就是 average.operator(3, 2, 3),这使得 average 看上去像函数的名字,故称其为函数对象。

函数对象应用实例1:在 accumulate 算法中的应用

STL 中有以下实现“累加”功能的算法(函数模板):

template <class InIt, class T, class Pred>T accumulate(InIt first, InIt last, T val, Pred op);

该模板的功能是对 [first, last) 中的每个迭代器 I 执行 val = op(val, *I),返回最终的 val。在 Dev C++ 中,numeric 头文件中 accumulate 的源代码如下:

template <class InIt, class T, class Pred>T accumulate(InIt first, Init last, T init, Pred op){  for (; first != last; ++first)    init = op(init, *first);  return init;};

此模板被实例化后,op(init, *first)必须要有定义,则 op 只能是函数指针或者函数对象。因此调用该 accmulate 模板时,形参 op 对应的实参只能是函数名、函数指针或者函数对象。

下面的程序通过 accumulate 模板求一个 vector 中元素的平方和,其中用到了函数对象。

#include <iostream>#include <vector>#include <numeric> //accumulate 在此头文件定义using namespace std;template <class T>void PrintInterval(T first, T last){ //输出区间[first,last)中的元素  for (; first != last; ++first)    cout << *first << " ";  cout << endl;}int SumSquares(int total, int value){  return total + value * value;}template<class T>class SumPowers{private:  int power;public:  SumPowers(int p) :power(p) { }  const T operator() (const T & total, const T & value)  { //计算 value的power次方,加到total上    T v = value;    for (int i = 0; i < power - 1; ++i)      v = v * value;    return total + v;  }};int main(){  const int SIZE = 10;  int a1[] = { 1,2,3,4,5,6,7,8,9,10 };  vector<int> v(a1, a1 + SIZE);  cout << "1) "; PrintInterval(v.begin(), v.end());  int result = accumulate(v.begin(), v.end(), 0, SumSquares);  cout << "2) 平方和:" << result << endl;  result = accumulate(v.begin(), v.end(), 0, SumPowers<int>(3));  cout << "3) 立方和:" << result << endl;  result = accumulate(v.begin(), v.end(), 0, SumPowers<int>(4));  cout << "4) 4次方和:" << result;  return 0;}

程序的输出结果如下:
1)1 2 3 4 5 6 7 8 9 10
2)平方和:385
3)立方和3025
4)4次方和:25333

第 37 行,第四个参数是 SumSquares 函数的名字。函数名字的类型是函数指针,因此本行将 accumulate 模板实例化后得到的模板函数定义如下:

int accumulate(vector <int>::iterator first, vector <int>::iterator last, int init, int(*op)(int, int)){  for (; first != last; ++first)    init = op(init, *first);  return init;}

形参 op 是一个函数指针,而op(init, *first)就调用了指针 op 指向的函数,在第 37 行的情况下就是函数 SumSquares。

第 39 行,第四个参数是 SumPowers<int>(3)。SumPowers 是类模板的名字,SumPowers<int> 就是类的名字。类的名字后面跟着构造函数的参数列表,就代表一个临时对象。因此 SumPowers<int>(3) 就是一个 SumPowers<int> 类的临时对象。

编译器在编译此行时,会将 accumulate 模板实例化成以下函数:

int accumulate(vector<int>::iterator first, vector<int>::iterator last, int init, SumPowers<int> op){  for (; first != last; ++first)    init = op(init, *first);  return init;}

形参 op 是一个函数对象,而op(init, *first)等价于:

op.operator()(init, *first);

即调用了 SumPowers<int> 类的 operator() 成员函数。

对比 SumPowers 和 SumSquares 可以发现,函数对象的 operator() 成员函数可以根据对象内部的不同状态执行不同操作,而普通函数就无法做到这一点。因此函数对象的功能比普通函数更强大。

函数对象应用实例2:在sort算法中的应用

STL 中的排序模板 sort 能将区间从小到大排序。sort 算法有两个版本。第一个版本的原型如下:

template <class_Randlt>void sort(_Randlt first, _RandIt last);

该模板可以用来将区间 [first, last) 中的元素从小到大排序,要求 first、last 是随机访问迭代器。元素比较大小是用<进行的。如果表达式a<b的值为 true,则 a 排在 b 前面;如果a<b的值为 false,则 b 未必排在 a 前面,还要看b<a是否成立,成立的话 b 才排在 a 前面。要使用这个版本的 sort 算法,待排序的对象必须能用<运算符进行比较。

sort 算法第二个版本的原型如下:

template <class_Randlt, class Pred>void sort(_Randlt first, _RandIt last, Pred op);

这个版本和第一个版本的差别在于,元素 a、b 比较大小是通过表达式op(a, b)进行的。如果该表达式的值为 true,则 a 比 b 小;如果该表达式的值为 false,也不能认为 b 比 a 小,还要看op(b, a)的值。总之,op 定义了元素比较大小的规则。下面是一个使用 sort 算法的例子。

#include <iostream>#include <alGorithm> //sort算法在此头文件中定义using namespace std;template <class T>void Printlnterva1(T first, T last){ //用以输出 [first, last) 区间中的元素  for (; first != last; ++first)    cout << *first << " ";  cout << endl;}class A{public:  int v;  A(int n) : v(n) {}};bool operator < (const A & a1, const A & a2){ //重载为 A 的 const 成员函数也可以,重载为非 const 成员函数在某些编译器上会出错  return a1.v < a2.v;}bool GreaterA(const A & a1, const A & a2){ //v值大的元素作为较小的数  return a1.v > a2.v;}struct LessA{  bool operator() (const A & a1, const A & a2)  { //v的个位数小的元素就作为较小的数    return (a1.v % 10) < (a2.v % 10);  }};ostream & operator << (ostream & o, const A & a){  o << a.v;  return o;}int main(){  int a1[4] = { 5, 2, 4, 1 };  A a2[5] = { 13, 12, 9, 8, 16 };  sort(a1, a1 + 4);  cout << "1)"; Printlnterva1(a1, a1 + 4); //输出 1)1 2 4 5  sort(a2, a2 + 5); //按v的值从小到大排序  cout << "2)"; Printlnterva1(a2, a2 + 5); //输出 2)8 9 12 13 16  sort(a2, a2 + 5, GreaterA); //按v的值从大到小排序  cout << "3)"; Printlnterva1(a2, a2 + 5); //输出 3)16 13 12 9 8  sort(a2, a2 + 5, LessA()); //按v的个位数从小到大排序  cout << "4)"; Printlnterva1(a2, a2 + 5); //输出 4)12 13 16 8 9  return 0;}

编译至第 45 行时,编译器将 sort 实例化得到的函数原型如下:

void sort(A* first, A* last, bool (*op)(const A &, const A &) );

该函数在执行过程中,当要比较两个元素 a、b 的大小时,就是看 op(a, b) 和 op(b, a) 的返回值。本程序中 op 指向 GreaterA,因此就用 GreaterA 定义的规则来比较大小。

编译至第 47 行时,编译器将 sort 实例化得到的函数原型如下:

void sort( A* first, A* last, LessA op);

该函数在执行过程中,当要比较两个元素 a、b 的大小时,就是看 op(a, b) 和 op(b, a) 的返回值。本程序中,op(a, b) 等价于 op.opeartor(a, b),因此就用 LessA 定义的规则来比较大小。

STL 中定义了一些函数对象类模板,都位于头文件 functional 中。例如,greater 模板的源代码如下:

template <class T>struct greater{  bool operator()(const T& x, const T& y) const{    return x > y;  }};

假设有以下数组

int a[4] = {3, 5, 34, 8};

要将该数组从大到小排序,则只需写:

sort( a, a+4, greater<int>() );

要使用 greater 模板,须确保>运算符本来就有定义,或经过了适当的重载。

list 容器的 sort 成员能将元素从小到大排序。它也有两个版本:一个是没有参数的函数,比较大小用<运算符;另一个是函数模板,原型如下:

template <class Pred>void sort(Pred op);

sort 函数允许自定义比较大小的规则,即 op(x, y) 为真就认为 x 比 y 小。例如,假设有:

list<int> lst;

如果希望将 lst 中的元素按其整数数值从大到小排序,只需写:

lst.sort( greater<int>() );

在使用关联容器和许多算法时,都可以用函数对象来定义比较大小的规则,以及其他一些规则和操作。

STL 中的函数对象类模板

STL 中有一些函数对象类模板,如表 1 所示。

表1:STL 中的函数对象类模板

函数对象类模板成员函数 T operator ( const T & x, const T & y) 的功能
plus <T>return x + y;
minus < >return x - y;
multiplies <T>return x * y;
divides <T>return x / y;
modulus <T>return x % y;
 成员函数 bool operator( const T & x, const T & y) 的功能
equal_to <T>return x == y;
not_equal_to <T>return x! = y;
greater <T>return x > y;
less <T>return x < y;
greater_equal <T>return x > = y;
less_equal <T>return x <= y;
logical_and <T>return x && y;
logical_or <T>return x || y;
 成员函数 T operator( const T & x) 的功能
negate <T>return - x;
 成员函数 bool operator( const T & x) 的功能
logical_not <T>return ! x;

例如,如果要求两个 double 型变量 x、y 的乘积,可以写:

multiplies<double> () (x, y)

less 是 STL 中最常用的函数对象类模板,其定义如下:

template <class_Tp>struct less{  bool operator() (const_Tp & __x, const_Tp & __y) const  { return __x < __y; }};

要判断两个 int 变量 x、y 中 x 是否比 y 小,可以写:

if( less<int>()(x, y) ) { ... }

引入函数对象后 STL 中的“大”、“小”和“相等”概念

前面提到过,默认情况下,STL 中的容器和算法比较元素的大小是通过<运算符进行的。通过 10.3.4 节可知,sort 和 list::sort 都可以通过一个函数对象或函数自定义比较元素大小的规则。例如以下的 sort 版本:

template <class_RandIt, class Pred>void sort(_RandIt first, _RandIt last, Pred op);

实际调用 sort 时,和 op 对应的实参可以是一个函数对象或者函数的名字。sort 在执行过程中用 op(x, y) 比较 x 和 y 的大小,因此可以将 op 称为自定义的“比较器”。

关联容器中的元素是从小到大排序的。使用关联容器时,也可以用自定义的比较器取代<运算符,以规定元素之间的大小关系。STL 中还有许多算法都可以自定义比较器。在自定义比较器 op 的情况下,以下三种说法是等价的:

  • x 小于 y。

  • op(x, y) 的返回值为 true。

  • y 大于 x。

同样地,对关联容器的 find 和 count 成员函数以及其他一些在有序区间上的 STL 算法而言,在自定义比较器 op 的情况下,x和y相等与op(x, y)和op(y, x)都为假是等价的。

关于一文读懂C++中的函数对象就分享到这里了,希望以上内容可以对大家有一定的帮助,可以学到更多知识。如果觉得文章不错,可以把它分享出去让更多的人看到。

--结束END--

本文标题: 一文读懂C++中的函数对象

本文链接: https://lsjlt.com/news/248166.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

猜你喜欢
  • 一文读懂C++中的函数对象
    这篇文章给大家介绍一文读懂C++中的函数对象,内容非常详细,感兴趣的小伙伴们可以参考借鉴,希望对大家能有所帮助。如果一个类将()运算符重载为成员函数,这个类就称为函数对象类,这个类的对象就是函数对象。函数对象是一个对象,但是使用的形式看起来...
    99+
    2023-06-06
  • 一文读懂java中的面向对象
    这期内容当中小编将会给大家带来有关一文读懂java中的面向对象,文章内容丰富且以专业的角度为大家分析和叙述,阅读完这篇文章希望大家可以有所收获。无论面向对象还是面向过程, 这俩都是解决问题的思路而已, 只是角度不同.面向过程:强调解决问题的...
    99+
    2023-05-31
    面向对象 java ava
  • 一文读懂C++ 虚函数 virtual
    探讨 C++ 虚函数 virtual 有无虚函数的对比 C++ 中的虚函数用于解决动态多态问题,虚函数的作用是允许在派生类中重新定义与积累同名的函数,并且可以通过基类指针或引用来访问...
    99+
    2024-04-02
  • 一文带你读懂java中的对象拷贝
    本篇文章为大家展示了一文带你读懂java中的对象拷贝,内容简明扼要并且容易理解,绝对能使你眼前一亮,通过这篇文章的详细介绍希望你能有所收获。java对象拷贝详解及实例Java赋值是复制对象引用,如果我们想要得到一个对象的副本,使用赋值操作是...
    99+
    2023-05-31
    java 对象拷贝 ava
  • 【Servlet篇】一文带你读懂Request对象
    文章目录 1. 前言 2. Request 对象 2.1 Request 继承体系 2.2 Request 获取请求参数 1. 获取请求...
    99+
    2023-10-22
    servlet java tomcat
  • 一文搞懂c++中的std::move函数
    目录前言左值和右值左值引用右值引用std::move函数remove_reference源码剖析std::forward源码剖析std::move()源码剖析小结std::move使...
    99+
    2024-04-02
  • 一文搞懂C#实现读写文本文件中的数据
    【1】首先我们定义一段假数据,这里以一个string为例字   static void Main(string[] args) { string data = "我的数据要开始...
    99+
    2024-04-02
  • 一文搞懂Java中对象池的实现
    目录1. 什么是对象池2. 为什么需要对象池3. 对象池的实现4. 开源的对象池工具5. JedisPool 对象池实现分析6. 对象池总结最近在分析一个应用中的某个接口的耗时情况时...
    99+
    2024-04-02
  • 【C++漂流记】一文搞懂类与对象的封装
    本篇文章主要说明了类与对象中封装的有关知识,包括属性和行为作为整体、访问权限、class与struct的区别、成员属性的私有化,希望这篇文章可以帮助你更好的了解类与对象这方面的知识。 ...
    99+
    2023-09-17
    c++ 前端 开发语言
  • 一文带你读懂MyBatis中的参数
    一文带你读懂MyBatis中的参数?相信很多没有经验的人对此束手无策,为此本文总结了问题出现的原因和解决方法,通过这篇文章希望你能解决这个问题。前言相信很多人可能都遇到过下面这些异常:"Parameter 'xxx'...
    99+
    2023-05-31
    mybatis
  • 一文读懂 MySQL 中的索引
    文章目录 1. 索引概述1.1 索引概述1.2 优点1.3 缺点1.6 常见索引概念1.6.1 聚簇索引1.6.2 二级索引(辅助索引、非聚簇索引)1.6.3 联合索引 1.8 MyISAM索引的原理1.9 MyISAM 与 ...
    99+
    2023-08-16
    mysql android 数据库
  • 一文读懂Android Kotlin的数据流
    目录一、Android分层架构二、ViewModel + LiveData2.1 LiveData 特性观察者的回调永远发生在主线程仅持有单个且最新数据自动取消订阅提供「可读可写」和...
    99+
    2024-04-02
  • 一文读懂C++中指针和内存分配
    指针 指针是保存内存位置地址的变量。我们知道声明的所有变量在内存中都有一个特定的地址。声明一个指针变量来指向内存中的这些地址。 声明指针变量的一般语法是: int p, *ptr...
    99+
    2024-04-02
  • 一篇文章读懂Golang init函数执行顺序
    目录1.init 函数简介2.执行顺序2.1 单个源文件的 init 执行顺序2.2 单个包的 init 执行顺序2.3 main 包导入多个包时 init 执行顺序2.3.1 不存...
    99+
    2024-04-02
  • 一文带你搞懂PHP对象注入
    目录背景漏洞案例PHP类和对象php magic方法php对象序列化序列化magic函数php对象注入常见的注入点其他的利用方法如何利用或者避免这个漏洞结论背景 php对象注入是一个...
    99+
    2024-04-02
  • 一文读懂 PHP SPL 扩展,成为面向对象编程高手
    PHP SPL 扩展简介 PHP SPL 扩展是 PHP 的一个标准扩展,它提供了一系列面向对象编程的工具和方法,有助于开发人员创建更加结构化、可维护的代码。SPL 扩展中的类和接口可以帮助开发人员处理各种常见的数据结构,例如数组、链表、...
    99+
    2024-02-03
    PHP SPL 标准 PHP 面向对象编程 接口 函数
  • 一起来学习C++的函数指针和函数对象
    目录函数指针函数对象总结函数指针 以下是<cstdlib>库中的一个排序数组的方法qsort()的函数原型。 void qsort (void* base, size_t...
    99+
    2024-04-02
  • C++ 中函数指针与函数对象的异同?
    函数指针和函数对象都是处理函数作为数据的机制。函数指针是指向函数的指针,而函数对象是包含重载的 operator() 的对象。两者都可以捕获变量并创建闭包。不同之处在于,函数指针是原始类...
    99+
    2024-04-12
    函数指针 函数对象 c++
  • C++:函数对象,STL提供的函数对象,函数适配器详解
    目录1 函数对象2 STL提供的函数对象3 函数适配器总结1 函数对象 1.函数对象是行为类似函数的对象。一个类对象,表现出一个函数的特征,即通过对象名+(参数列表)的方式使用一个类...
    99+
    2024-04-02
  • 一文读懂Java中的异常处理
    这篇文章将为大家详细讲解有关一文读懂Java中的异常处理,文章内容质量较高,因此小编分享给大家做个参考,希望大家阅读完这篇文章后对相关知识有一定的了解。一、什么是异常     异常的英文单词是exce...
    99+
    2023-05-31
    java 异常处理 ava
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作