将函数作为数值
在完成的编程中,函数和数据结构的概念仍然是分开的。函数提供表示算法的方法; 数据结构允许你组织并应用这些算法的信息。这样说来函数已经是算法结构的一部分,而不是数据结构的一部分。然而,能够使用函数作为数据值,通常会使设计有效的接口更容易,因为这样做允许客户端指定操作和数据。
函数指针
在计算的最初阶段,程序的表现形式使其完全与数据分开。通常,在纸带上打印指令,然后送入机器,然后依次执行指令。如果你想改变程序,你就不得不打一个新的纸带。现代计算机最重要的特征之一是用于存储数据值的相同存储字也用于存储由硬件执行的机器语言指令。在用于数据值的存储器地址中存储指令的这种技术在数学家约翰·冯·诺依曼(John von Neumann)之后称为冯·诺依曼(von Neumann)架构。虽然计算历史学家现在认为,冯·诺依曼不太可能产生这个想法,但他似乎是第一个发表这个想法的,而这个概念仍然是他的名字。
冯·诺依曼结构的重要影响之一是程序中的每个机器语言指令都有一个内存地址。这个事实使得可以创建一个指向一个函数的指针,这只是它的第一个指令的地址。大多数现代化的面向对象语言在内部使用指针来隐藏程序的细节。相比之下,C ++使程序员可以声明指向函数的指针,然后将这些函数用作应用程序中的数据值。
声明指向函数的指针
要考虑的下一个问题是如何声明指向函数的指针。 C ++使用句法形式来声明一个指向的函数的指针,但实际上与其他声明表单一致。
例如,如果你想要声明一个变量fn是一个指向函数并返回一个double的指针,你可以通过写:
double (*fn)(double);
注意:在函数指针的声明中记住* fn周围的括号很重要。 下面的这一行代码:
double *fn(double);
是声明fn作为将指针返回到double的函数。
这两个概念确实有点难以理解,那么我们举个实例来试试
比较函数
将函数作为数据来使用的一个例子就是实现比较函数。当你要使用关系运算符<,==和>提供的自然数排序以外的某些排序方案对vector或数组进行排序时。 在这种情况下,通常的方法是将比较函数传递给实现新排序的排序函数。 比较函数通常取两个值v1和v2,并返回一个具有以下属性的整数:
- 如果v1和v2相等,则比较函数返回0。
- 如果v1在v2之前,比较函数返回小于0的数字。
- 如果v1在v2之后,比较函数返回大于0的数字
下面的代码定义了一个排序函数,它根据客户端提供的比较函数和一个sortIgnoringCase函数对字符串的向量进行排序,该函数比较两个字符串而不考虑大小写。调用:
sort(vec, sortIgnoringCase);
因此排序一个字符串向量,而不考虑字母的大小写,这大概是大多数按字母表排列的程序所需要的。
代码展示
#include <iostream>
#include <string>
#include <vector>
#include "strlib.h"
using namespace std;
/*函数原型*/
void sort(vector<string> & vec, int (*cmp)(string, string));
int sortIgnoringCase(string s1, string s2);
/*主函数测试代码*/
int main(){
vector<string> vec;
for(int i = 0; i < 3; i++){
string str;
cin >> str;
vec.push_back(str);
}
sort(vec, sortIgnoringCase);
for(int k = 0; k < 3; k++){
cout << vec[k] << " ";
}
}
/*
*函数:sort
*用法:sort(vec,cmp);
*------------------------
*使用客户指定的比较函数cmp对字符串向量进行排序,以确定排序。
*比较函数采用两个字符串,s1和s2返回一个整数,其符号表示比较结果。
*如果s1和s2相等,则比较函数必须返回0.如果s1在顺序中位于s2之前,
*则比较函数必须返回小于0的整数。如果s1在s2之后,则比较函数返回大于0的整数。
*这个函数的第二个函数就是cmp函数得出的结果通过指针传递到sort函数。
*/
void sort(vector<string> & vec, int (*cmp)(string, string)) {
int n = vec.size();
for (int lh = 0; lh < n; lh++) {
int rh = lh;
for (int i = lh + 1; i < n; i++) {
if (cmp(vec[i], vec[rh]) < 0) rh = i;
}
string tmp = vec[lh];
vec[lh] = vec[rh];
vec[rh] = tmp;
}
}
/*
*函数: sortIgnoringCase;
*用法: sortIgnoringCase(s1,s2);
*--------------------------------
*我们这里就是把这个函数的结果作为sort的第二个参数。
*返回一个整数,其符号表示字符串s1和s2之间的关系,而不考虑情况。
*在大多数情况下,这个函数不是显式调用的,而是传递给sort函数或者
*其他一些需要的函数比较功能。该实现采用将整个字符串转换为小写的简单但低效
*的策略,然后使用字符串类的关系运算符比较结果。
*/
int sortIgnoringCase(string s1, string s2) {
s1 = toLowerCase(s1);
s2 = toLowerCase(s2);
if (s1 == s2) return 0;
if (s1 < s2) return -1;
return 1;
}
关于strlib.h头文件,参考C++抽象编程——自定义strlib文件
结果示例
拓展
sortIgnoringCase函数只是可以与此扩展版本排序相关联的许多功能之一。 例如,如果你希望按字符串长度排序,则可以对以下比较函数进行排序:
int sortByLength(string s1, string s2) {
return s1.length() - s2.length();
}
减法运算符就是我们所需要的。如果s1短于s2,该函数将返回一个负数。类似地,如果s1长于s2,结果将为正数。如果长度匹配,则sortByLength函数返回0。
另一个例子,你可以通过使用以下比较函数调用排序来反向字母表的顺序排序字符串:
int sortBackwards(string s1, string s2) {
if (s1 == s2) return 0;
if (s1 > s2) return -1;
return 1;
}
我就只是测试第一个长度的代码,过程不写了,就贴个结果
注意: 这样把函数作为数据传递的好处是我们可以按需求定置我们的功能,只要函数返回的是一个数据,(当然bool变量也算在其中),我们都可以用函数指针指向其结果,用于其他函数,我记得我在写STL的删除处理的时候,就写过一个 remove_if 函数的介绍,里面用到的就是这个机制!!
remove_if()函数 参看:
C++抽象编程——STL实战(3)——数组元素的移除与合并