一起来学习C++的函数指针和函数对象

目录
  • 函数指针
  • 函数对象
  • 总结

函数指针

以下是<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 );
}
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
#include <cstdio>
#include <cstring>
#include <string>
#include <cctype>
#include <iostream>
#define LEN 81
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 >> line) {
		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());
	}
	return ans;
}
void show(void (* fp)(char*), char* str) {
	(*fp)(str); // 把用户选定的函数作用于str
	cout << 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) {
} //不改变字符串

函数对象

函数对象是专门设计用于语法与函数相似的对象。在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开始的范围中。

该函数允许目标范围与其中一个输入范围相同,以便进行适当的转换。

函数对象适配器:

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, 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.

总结

本篇文章就到这里了,希望能够给你带来帮助,也希望您能够多多关注我们的更多内容!

(0)

相关推荐

  • 如何应用C++的函数对象

    前言 C++函数对象是通过一张虚函数表来实现的.简称为V-Table.在这个表中,主是要一个类的虚函数的地址表,这张表解决了继承.重载的问题,保证其容真实反应实际的函数. 应用 假如我们实现了这样的一个单向链表: class LinkedListNode { int data_; LinkedListNode *next_; }; class LinkedList { public: void insert(LinkedListNode* &p); void del(LinkedListNode

  • C++ 函数指针详细总结

    目录 1.函数指针 1.1 获取函数地址 1.2 声明函数指针 1.3 函数指针传参 1.4 调用函数 2.函数指针进阶 1.函数指针 函数指针顾名思义,就是指向函数的指针. 和数据类似,C++当中函数也有地址,函数的地址是存储函数机器语言代码的内存地址.我们可以将另外一个函数的地址作为参数传入函数,从而实现函数的灵活调用. 1.1 获取函数地址 获取函数地址的方法非常简单,只要使用函数名(后面不跟参数和括号)即可.比如我们有一个函数叫做think,那么think()是调用函数拿到结果,而thi

  • C++函数指针详解

    函数指针基础: 1. 获取函数的地址 2. 声明一个函数指针 3.使用函数指针来调用函数 获取函数指针: 函数的地址就是函数名,要将函数作为参数进行传递,必须传递函数名. 声明函数指针 声明指针时,必须指定指针指向的数据类型,同样,声明指向函数的指针时,必须指定指针指向的函数类型,这意味着声明应当指定函数的返回类型以及函数的参数列表. 例如: double cal(int); // prototype double (*pf)(int); // 指针pf指向的函数, 输入参数为int,返回值为d

  • C++中的函数指针与函数对象的总结

    篇一.函数指针函数指针:是指向函数的指针变量,在C编译时,每一个函数都有一个入口地址,那么这个指向这个函数的函数指针便指向这个地址. 函数指针的用途是很大的,主要有两个作用:用作调用函数和做函数的参数. 函数指针的声明方法:数据类型标志符 (指针变量名) (形参列表):一般函数的声明为: int func ( int x );而一个函数指针的声明方法为:int (*func) (int x);前面的那个(*func)中括号是必要的,这会告诉编译器我们声明的是函数指针而不是声明一个具有返回型为指针

  • 详解C语言编程中的函数指针以及函数回调

    函数指针: 就是存储函数地址的指针,就是指向函数的指针,就是指针存储的值是函数地址,我们可以通过指针可以调用函数. 我们先来定义一个简单的函数: //定义这样一个函数 void easyFunc() { printf("I'm a easy Function\n"); } //声明一个函数 void easyFunc(); //调用函数 easyFunc(); //定义这样一个函数 void easyFunc() { printf("I'm a easy Function\n

  • 一起来学习C++的函数指针和函数对象

    目录 函数指针 函数对象 总结 函数指针 以下是<cstdlib>库中的一个排序数组的方法qsort()的函数原型. void qsort (void* base, size_t num, size_t size, int (*compar)(const void*, const void*)); base -- 指向要排序的数组的第一个元素的指针. num -- 由 base 指向的数组中元素的个数. size -- 数组中每个元素的大小,以字节为单位. compar -- 用来比较两个元素

  • 如何通过函数指针调用函数(实现代码)

    说明:指针可以不但可以指向一个整形,浮点型,字符型,字符串型的变量,也可以指向相应的数组,而且还可以指向一个函数. 一个函数在编译的时候会被分配给一个入口地址.这个函数入口地址称为函数的指针.可以用一个指针变量指向函数,然后通过该指针变量调用此函数. 定义指向函数的指针变量的方法是: 复制代码 代码如下: int (*p) (int ,int ); int[指针变量p指向的函数的类型] (*p)[p是指向函数的指针变量] ( int,int )[p所指向的形参类型]; 与函数的原型进行比较 复制

  • C语言中的函数指针基础学习教程

    顾名思义,函数指针就是函数的指针.它是一个指针,指向一个函数.看例子: A) char * (*fun1)(char * p1,char * p2); B) char * *fun2(char * p1,char * p2); C) char * fun3(char * p1,char * p2); 看看上面三个表达式分别是什么意思? C)这很容易,fun3是函数名,p1,p2是参数,其类型为char *型,函数的返回值为char *类型. B) 也很简单,与C)表达式相比,唯一不同的就是函数的

  • C语言中的函数指针学习笔记

    一.定义函数指针 return_type (*func_pointer)(parameter_list) 普通指针变量的定义 int * p; char * pointer; 类型的限定都在变量前面: 函数指针类型的限定是前后都有,前面是返回类型,后面是输入参数. 利用typedef 可以简化上面的表达方式. typedef return_type (*FunctionPointer) (parameter_list); FunctionPointer func_pointer; 这样是不是容易

  • 函数指针与指针函数的学习总结

    函数指针是指向函数的指针,指针函数是指一个函数的返回值是一个指针,但下面的几道题还是感觉很迷惑.各位能否讲的详细点呢? (1) float(**def)[10]   def是什么?(2) double*(*gh)[10]   gh是什么?(3) double(*f[10])()   f是什么?(4) int*((*b)[10])    b是什么?这样老感觉有点乱,有什么窍门可以记得并理解的清楚一点么? ======================解答:   (1) def是一个指针, 指向的对象

  • 详解C++中的指针、数组指针与函数指针

    C++中一个重要的特性就是指针,指针不仅具有获得地址的能力,还具有操作地址的能力.指针可以用于数组.或作为函数的参数,用来访问内存和对内存的操作,指针的使用使得C++很高效,但是指针也非常危险,使用不当会带来比较严重的问题. 1.指针 程序中所有的变量和常量都存在一个内存地址中,当然,函数也有对应的内存地址,内存地址的不同会导致程序执行时有所不同. 指针就是用来控制和存储内存地址的变量,它指向单个对象的地址,除了void之外,指针的数据类型与所指向地址的变量数据类型保持一致. 2.如何定义指针.

  • C语言中函数指针与软件设计经验总结

    函数指针与软件设计 记得刚开始工作时,一位高手告诉我,说,longjmp和setjmp玩得不熟,就不要自称为C语言高手.当时我半信半疑,为了让自己向高手方向迈进,还是花了一点时间去学习longjmp和setjmp的用法.后来明白那不单是跳来跳去那样简单,而是一种高级的异常处理机制,在某些情况下确实很有用. 为了显示自己的技巧,也在自己的程序中用过几次.渐渐发现这样的技巧带来的好处是有代价的,破坏了程序的结构化设计,程序变得很难读,尤其对新手来说.终于明白这种技巧不过是一种调味料,在少数情况使用几

  • C语言运用函数指针数组实现计算器功能

    本文实例为大家分享了C语言运用函数指针数组制作计算器的具体代码,供大家参考,具体内容如下 先来回顾一下概念: 指针数组 -- 存放指针的数组 函数指针 -- 存放函数地址的指针 函数指针数组 -- 存放函数指针的数组 接下来说说这次要制作的计算器的功能: 1.add -- 加法 2.sub -- 减法 3.mul -- 乘法 4.div -- 除法 0.exit -- 退出 具体来通过代码讲解: (1)首先写一个菜单程序,在运行程序时首先打印一次菜单. void menu() { printf(

随机推荐