函数指针

以下是<cstdlib>库中的一个排序数组的方法qsort()的函数原型。

void qsort (void* base, size_t num, size_t size,int (*compar)(const void*, const void*));
  • base -- 指向要排序的数组的第一个元素的指针。
  • num -- 由 base 指向的数组中元素的个数。
  • size -- 数组中每个元素的大小,以字节为单位。
  • compar -- 用来比较两个元素的函数。

对于可以使用常规关系运算符进行比较的类型,常规比较函数可能如下所示:

int compareMyType (const void * a, const void * b) {if ( *(MyType*)a <  *(MyType*)b ) return -1;if ( *(MyType*)a == *(MyType*)b ) return 0;if ( *(MyType*)a >  *(MyType*)b ) return 1;
}
#include <cstdlib>
#include <iostream>
int cmpfunc (const void* a, const void* b);
using namespace std;
int main() {int values[] = { 88, 56, 100, 2, 25 };qsort(values, sizeof(values)/sizeof(int), sizeof(int), cmpfunc);cout << "排序之后的列表:" << endl;for(int n = 0 ; n < 5; n++ ) {cout << values[n] << " ";}return 0;
}
int cmpfunc (const void* a, const void* b) {return ( *(int*)a - *(int*)b );
}
#include <cstdio>
#include <cstring>
#include <string>
#include <cctype>
#include <iostream>
#define LEN 81
void eatline();
char showmenu();
void show(void (* fp)(char*), char* str);
void ToUpper(char*); // 把字符串转换为大写
void ToLower(char*); // 把字符串转换为小写
void Transpose(char*); // 大小写转置
void Dummy(char*); // 不更改字符串
using namespace std;
int main() {char line[LEN];char copy[LEN];char choice;void (* pfun)(char*); // 声明一个函数指针, 被指向的函数接受char *类型的参数, 无返回值cout << "Enter a string (empty line to quit):";while (cin.getline(line, LEN) && line[0] != '\0') {while ((choice = showmenu()) != 'n') {switch (choice) { // switch语句设置指针case 'u':pfun = ToUpper;break;case 'l':pfun = ToLower;break;case 't':pfun = Transpose;break;case 'o':pfun = Dummy;break;}strcpy(copy, line); // 为show()函数拷贝一份show(pfun, copy); // 根据用户的选择, 使用选定的函数}cout << "Enter a string (empty line to quit):";}cout << "Bye!";return 0;
}char showmenu() {char ans;cout << "Enter menu choice:" << endl;cout << "u) uppercase l) lowercase" << endl;cout << "t) transposed case o) original case" << endl;cout << "n) next string" << endl;ans = getchar(); // 获取用户的输入ans = tolower(ans); // 转换为小写while (strchr("ulton", ans) == NULL) {cout << "Please enter u, l, t, o, or n:" << endl;ans = tolower(getchar());eatline();}return ans;
}void eatline() {while (getchar() != '\n')continue;
}
void show(void (* fp)(char*), char* str) {(*fp)(str); // 把用户选定的函数作用于strcout << str << endl; // 显示结果
}
void ToUpper(char* str) {while (*str) {*str = toupper(*str);str++;}
}
void ToLower(char* str) {while (*str) {*str = tolower(*str);str++;}
}
void Transpose(char* str) {while (*str) {if (islower(*str))*str = toupper(*str);else if (isupper(*str))*str = tolower(*str);str++;}
}
void Dummy(char* str) {
} //不改变字符串
Enter a string (empty line to quit):|abc<Enter>
Enter menu choice:
u) uppercase l) lowercase
t) transposed case o) original case
n) next string
Please enter u, l, t, o, or n:
|u<Enter>
ABC
Enter menu choice:
u) uppercase l) lowercase
t) transposed case o) original case
n) next string
Please enter u, l, t, o, or n:
|l<Enter>
abc

这种情况下, 可以使用typedef。 例如, 该程序中可以这样写:

typedef void (*V_FP_CHARP)(char *);
void show (V_FP_CHARP fp, char *);
V_FP_CHARP pfun;

如果还想更复杂一些, 可以声明并初始化一个函数指针的数组:

V_FP_CHARP arpf[4] = {ToUpper, ToLower, Transpose, Dummy};

然后把showmenu()函数的返回类型改为int, 如果用户输入u, 则返回0;如果用户输入l, 则返回2; 如果用户输入t, 则返回3, 以此类推。 可以把程序中的switch语句替换成下面的while循环:

index = showmenu();
while (index >= 0 && index <= 3) {strcpy(copy, line); /* 为show()拷贝一份 */show(arpf[index], copy); /* 使用选定的函数 */index = showmenu();
}

函数对象

函数对象是专门设计用于语法与函数相似的对象。在C++中,这是通过在类中定义成员函数operator()来实现的,例如:

struct myclass {int operator()(int a) {return a;}
} myobject;int x = myobject(0);

它们通常用作函数的参数,例如传递给标准算法的谓词或比较函数。

标准库预先定义了些function object。所谓function object,是某种class的实例对象,这类class对function call运算符做了重载操作,如此一来可使function object被当成一般函数来使用。

function object实现了我们原本可能以独立函数加以定义的事物。但又何必如此呢?
主要是为了效率。我们可以令call运算符成为inline,从而消除“通过函数指针来调用函数”时需要付出的额外代价。

标准库事先定义了一组function object,分为:
算术运算(arithmetic)、关系运算(relational)和逻辑运算(logical)三大类。

以下列表中的type在实际使用时会替换为内置类型或class类型:

6个算术运算

plus<type>,minus<type>,negate<type>,

multiplies<type>,divides<type>,modules<type>

6个关系运算

less<type>,less_equal<type>,greater<type>,

greater_equal<type>,equal_to<type>,not_equal_to<type>

3个逻辑运算 logical_and<type>,logical_or<type>,logic_not<type>

要使用事先定义的function object,首先得包含相关头文件:<functional>

默认情况下sort()是升序排列,我们将元素降序排列:

sort(vec.begin(), vec.end(), greater<int>());

其中的greater<int>()会产生一个未命名的class template object,传给sort()。
binary_search()期望其搜索对象先经过排序,为了正确搜索vector,就必须传给它某个function object object,供vector排序使用:

binary_search(vec.begin(), vec.end(), elem, greater<int>());

我们对Fibonacci数列可以做些其他操作,如:每个元素和自身相加、和自身相乘、被加到对应的Pell数列等等。做法之一是使用泛型算法transform()并搭配plus<int>和multiplies<int>。
我们必须传给transform()的参数有:
➀一对iterator,标示出欲转换的元素范围;
➁一个iterator,所指元素将应用于转换上,元素范围同➀;
➂一个iterator,所指位置(及其后面的空间)用来存放转换结果;
➃一个function object,表现出我们想要应用的转换操作。
以下是将Pell数列加到Fibonacci数列的写法:

transform(fib.begin(), fib.end(), //➀pell.begin(),           //➁fib_plus_pell.begin(),  //➂plus<int>);             //➃

transform()的定义:
function template <algorithm> std::transform

unary operation(1)
template <class InputIterator, class OutputIterator, class UnaryOperation>
OutputIterator transform(InputIterator first1, InputIterator last1,OutputIterator result, UnaryOperation op);
binary operation(2)
template <class InputIterator1, class InputIterator2,class OutputIterator, class BinaryOperation>
OutputIterator transform(InputIterator1 first1, InputIterator1 last1,InputIterator2 first2, OutputIterator result,BinaryOperation binary_op);
————————————————————————————————————————————————————
将操作顺序应用于一(1)或两(2)个范围的元素,并将结果存储在从结果开始的范围中。
(1) 一元操作
将op应用于[first1,last1]范围内的每个元素,并将每个操作返回的值存储在从result开始的范围内。
(2) 二元操作
使用范围[first1,last1]中的每个元素作为第一个参数,并使用范围中从first2开始的各个参数作为
第二个参数来调用binary_op。每个调用返回的值存储在从result开始的范围中。该函数允许目标范围与其中一个输入范围相同,以便进行适当的转换。
template <class T> struct less;
二元函数对象类,其调用返回其第一个参数的比较是否小于第二个参数(由运算符<返回)。
通常,函数对象是定义了成员函数运算符()的类的实例。此成员函数允许使用与函数调用相同的语法来使用对象。其定义与以下行为相同:
template <class T> struct less : binary_function <T,T,bool> {bool operator() (const T& x, const T& y) const {return x<y;}
};

函数对象适配器:

function object less<type>期望外界传入两个值,如果第一个值小于第二个值就返回true。本例中,每个元素都必须和用户所指定的数值进行比较。理想情形下,我们需要将less<type>转化为一个一元(unary)运算符。这可通过“将其第二个参数绑定(bind)至用户指定的数值”完成。这么一来less<type>便会将每个元素拿出来一一与用户指定的数值比较。
真的可以做到这样吗?是的。标准库提供adapter(适配器)便应此而生。

function object adapter会对function object进行修改操作。binder adapter(绑定适配器)会将function object的参数绑定至某特定值,使binary(二元) function object转化为unary(一元)function object。这正是我们需要的。

标准库提供了两个binder adapter
bind1st会将指定值绑定至第一操作数;bind2nd将指定值绑定至第二操作数。
如:a < b,则a是第一操作数,b是第二操作数。

vector<int> filter<const vector<int> &vec, int val, less<int> &lt) {vector<int> nvec;vector<int>::const_iterator iter = vec.begin();while ((iter = find_if(iter, vec.end(), bind2nd(lt, val))) != vec.end()) {nvec.push_back(*iter);iter++;}return nvec;
}

bind2nd(less<int>, val);会把val绑定于less<int>的第二个参数身上。于是,less<int>会将每个元素拿来和val比较。上例第一操作数是*iter,第二操作数就是固定值val。如果*iter<val则true。

find_if()的定义如下:

template <class InputIterator, class UnaryPredicate>
InputIterator find_if(InputIterator first, InputIterator last, UnaryPredicate pred);
●first、last:输入迭代器到序列的初始和最终位置。使用的范围是[first,last),它包含first和last之间的所有元素,包括first指向的元素,但不包括last指向的元素。
●pred:接受范围内的元素作为参数并返回可转换为bool类型的值的【一元函数】。返回的值表明该元素是否被认为是此函数的上下文中的匹配。 函数不能修改它的参数。 它可以是函数指针,也可以是函数对象(function object)。
●返回值:指向pred不返回false的范围内第一个元素的迭代器。 如果pred对所有元素都为false,则函数返回last。
这个函数模板的行为相当于:
template<class InputIterator, class UnaryPredicate>
InputIterator find_if(InputIterator first, InputIterator last, UnaryPredicate pred) {while (first!=last) {if (pred(*first)) return first;++first;}return last;
}

下面看一个泛型函数find_if()的例子:

#include <iostream>     // std::cout
#include <algorithm>    // std::find_if
#include <vector>       // std::vector
bool IsOdd (int i) {return ((i%2)==1);
}
int main () {std::vector<int> myvector;myvector.push_back(10);myvector.push_back(25);myvector.push_back(40);myvector.push_back(55);std::vector<int>::iterator it = std::find_if(myvector.begin(), myvector.end(), IsOdd);std::cout << "The first odd value is " << *it << '\n';return 0;
}
The first odd value is 25

看一个bind2nd()和bind1st()的例子:

#include <iostream>
#include <functional>
#include <algorithm>
using namespace std;
int main () {int numbers[] = {10,-20,-30,40,-50};int cx = count_if(numbers, numbers+5, bind2nd(less<int>(), 0));cout << "There are " << cx << " negative elements.\n";return 0;
}
There are 3 negative elements.
#include <iostream>
#include <functional>
#include <algorithm>
using namespace std;
int main () {int numbers[] = {10,-20,-30,40,-50};int cx = count_if(numbers, numbers+5, bind1st(less<int>(), 0));cout << "There are " << cx << " positive elements.\n";return 0;
}
There are 2 positive elements.

❥关于C++之函数指针函数对象相关推荐

  1. C/C++ 一段代码区分数组指针|指针数组|函数指针|函数指针数组

    1 #include<stdio.h> 2 #include<stdlib.h> 3 #include<windows.h> 4 /* 举列子说明什么是函数指针 * ...

  2. c语言map函数指针,函数指针 - 弧光守望者 - OSCHINA - 中文开源技术交流社区

    /*Author:Choas Lee *Date:2012-02-28 */ #include #include #include float add(float a,float b){return  ...

  3. C语言--返回指针的函数--指针函数

    人之所以痛苦,那是因为你在成长.--------magic_guo 函数可以返回整形.字符串.实型值.无返回值.也可以返回指针类型的数据,即返回一个内存地址,像这样返回地址的函数成为指针函数.其概念与 ...

  4. C语言 返回指针的函数--指针函数 int* max(int a)

    定义 strlong示例代码 代码1: #include<stdio.h> #include<string.h> //返回指针的函数//比较两个字符串,返回更长的字符串 cha ...

  5. C++中的函数指针和函数对象总结

    篇一.函数指针 函数指针:是指向函数的指针变量,在C编译时,每一个函数都有一个入口地址,那么这个指向这个函数的函数指针便指向这个地址. 函数指针的用途是很大的,主要有两个作用:用作调用函数和做函数的参 ...

  6. c++函数指针和函数对象

    C++ 函数指针和函数对象 一. 导述 二.c++成员函数在内存中的存储方式 补1:静态数据和常量的区别: 三.函数指针 四.函数对象 一. 导述 博主认为,要想正确深入了解c++中的函数类型和函数指 ...

  7. C++函数指针、函数对象与C++11 function对象对比分析

    1.函数指针 函数指针:是指向函数的指针变量,在C编译时,每一个函数都有一个入口地址,那么这个指向这个函数的函数指针便指向这个地址.函数指针主要由以下两方面的用途:调用函数和用作函数参数. 函数指针的 ...

  8. 【C++】 指向类的指针 指向类的对象 对象指针 函数指针 引用

    文章目录 C++类的对象和类的指针的区别 指向地址的指针 指针本身的大小 指向数组的指针 指针数组 指向指针数组的指针 多维指针数组 函数参数中使用指针 数组指针传址实现数组求和 函数指针 模仿C++ ...

  9. C#未来新特性:静态委托和函数指针

    C#每发布一次新版本,都会增加一些底层相关的新特性, 这些特性对大多数商业程序来说并没什么实际用处,主要用于那些对性能有很高要求的代码,如图形处理.机器学习以及数学工具包,等等. 接下来的两个提案,主 ...

最新文章

  1. BS-GX-018 基于SSM实现在校学生考试系统
  2. 定义指令时“控制器”,“链接”和“编译”函数之间的区别
  3. Oracle海量数据优化-02分区在海量数据库中的应用-更新中
  4. 动画,视频处理的计算机系统,音视频与动画处理.ppt
  5. 【若依(ruoyi)】Bootstrap-Table表格排序
  6. DSP F28335的SCI模块
  7. 一次基于Vue.Js的用户体验优化 (vue drag)
  8. 洛谷 P1469 找筷子
  9. 程序员后期,架构师发展路线!
  10. frp对http协议应用
  11. codeforces679C Bear and Square Grid(dfs优化)
  12. django-装饰器实现PV统计
  13. linux root权限不够_Linux基础篇之用户管理
  14. 【InnoDB】体系结构
  15. POJ 3133 Manhattan Wiring(插头DP)
  16. GPS定位技术相关的毕业论文有哪些呢?
  17. SDS启动失败,提示连接primary节点失败
  18. 什么叫机械硬盘_机械硬盘内部是什么结构?
  19. 爬虫学习笔记(三)——利用JSON爬取豆瓣热映的使用
  20. MD5大小写,32,16

热门文章

  1. 来世,别让我这么晚说爱你
  2. js 一个等号= 二个等号== 三个等号=== 的区别
  3. 三行代码解决electron-updater升级失败问题
  4. 打包后自定义attr找不到_80后长沙文物修复师刘胄造“精绝古城”,修复实力连X光都找不到破绽...
  5. 网站后台主流架构设计参考(图文)
  6. rxswift 网络请求_使用RxSwift将身份验证请求链接到多个服务
  7. 【操作系统的目标和作用】
  8. java net unicode / native2ascii / url decode / url encode / UTF8 / js url code
  9. php计算1-100奇数的和,学习脚本1:计算100以内奇数和和偶数和 (笔记)
  10. 2019年11月20日笔记