C++中函数指针、函数对象的概念以及使用
C/C++函数指针、函数对象
“不了解指针,就不了解C”
指针是C/C++中重要的概念,也是C/C++区别于其他编程语言的特点之一。
毫不夸张地说,指针之于C的重要性相当于对象之于Java的重要性一样。
指针的基本概念和相关语法在此不做赘述,需要学习的人前往 C++指针|菜鸟教程 学习。
在这里只介绍函数指针的概念和基本用法。
函数指针
函数指针就是指向函数的指针,通过指针访问函数。
与通过函数名访问函数不同的是,通过指针访问函数在很多时候可以简化代码,在一定程度上提高代码可读性。
2022年7月20日重读自己写的文章,提高代码可读性这个点纯属扯淡,函数指针最大的作用就是提供 函数参数化 的能力,即将一个函数过程作为参数进行抽象的能力,可以让一个函数接受一个函数指针,然后返回一个函数指针。其实在某种意义上来说,将所有的对象都视为函数也未尝不可,例如可以构造一个常数函数,将所有的数据都抽象成函数。有些人可能觉得这样做让人感到困惑,似乎让事情变得更复杂了。然而,将数据和过程进行统一是非常重要的思想。它暗示了这样一种抽象:即所有的对象都有被统一处理的可能性,针对所有的对象设计出统一的接口是有可能实现的。
例如,下面是一个利用函数指针求积分的例子,利用积分的定义(无限分割,这里用的是小矩形块)来求积分:
//Calculate用于计算积分。一共三个参数。第一个为函数指针func,指向待积分函数。二三参数为积分上(函数指针版) double Calculate_func(double(*func)(double x), double a, double b){ double dx = 0.00001; double sum = 0; for (double xi = a; xi <= b; xi += dx){ double area = func(xi) * dx; sum += area; } return sum; }
被积函数:
//函数f(x) = x^2 double func_1(double x){ return x * x; } //函数f(x) = x^3 double func_2(double x){ return x * x * x; }
main函数:
int main(){ cout << Calculate_func(func_1, 0, 1) << endl << Calculate_func(func_2, 0, 1); return 0; }
运行结果:

函数对象
函数对象是C++特有的玩意(重载运算符是真的可以让语言变骚,难怪Java禁止重载运算符)。
函数本身是面向过程的产物,而C++允许重载运算符,在一个类中重载了函数调用( )运算符后,这个类的对象就拥有函数的能力。
就像是连接了新旧两个世界一样,函数对象既可以用于封装数据和方法,本身也可以作为函数被调用。
如果将函数指针也一起运用起来,会产生一些意想不到的效果。
还是上面那个求积分的例子,这次我们用函数对象来封装一下,并且添加梯形分割方式:
/*函数对象版积分Calculate用于计算积分。一共三个参数。第一个为函数指针func,指向待积分函数。二三参数为积分上下限*/ class Calculate_class{ private: //dx自变量增量,区间细分精度 double dx; //sum定积分,记录与x轴围城的面积 double sum; //积分方式,0为矩形积分,1为梯形积分 int method; //矩形方式积分 double calcuAsRect(double (*func)(double x), double a, double b){ cout << "矩形分割积分调用" << endl; //防止重复调用同一实例化的对象导致的sum叠加现象 sum = 0; for (double xi = a; xi <= b; xi += dx){ //矩形切割,底面积乘高 double area = func(xi) * dx; sum += area; } return sum; } //梯形方式积分 double calcuAsTrape(double (*func)(double x), double a, double b){ cout << "梯形分割积分调用" << endl; sum = 0; for (double xi = a; xi <= b; xi += dx){ //梯形切割,(上底 + 下底) * 高 / 2 double area = (func(xi) +func(xi + dx)) * dx / 2; sum += area; } return sum; } public: //设置积分精度,t_dx为积分精度 void SetPrecision(double t_dx) { dx = t_dx; } //默认构造、构造函数,t_dx为积分精度 Calculate_class(double t_dx = 0.00001):dx(t_dx),sum(0){} //重载()运算符,构造函数对象 double operator()(double (*func)(double x), double a, double b, int method){ return method ? calcuAsTrape(func, a, b) : calcuAsRect(func, a, b) ; } };
main函数:
int main(){ Calculate_class claculate; //每次随机调用矩形或者梯形分割 srand((unsigned)time(NULL)); int method = rand() % 2; cout << claculate(func_1, 0, 1,method) << endl << claculate(func_2, 0, 1,method) << endl; // cout << Calculate_func(func_1, 0, 1) << endl // << Calculate_func(func_2, 0, 1); return 0; }
运行结果:

函数对象Calculate_class封装了两种求积分的基本方法:矩形分割法和梯形分割法,通过传入整数method来选择确定的积分方法,得到了一个封装的类。
如果有需要的话,还可以将被积函数全部封装到一个类MyFuc中,将其中的所有函数声明为static,完成简单的封装
class MyFunc { public: static double func_1(double x) { return x * x; } static double func_2(double x) { return x * x * x; } };
main函数改为:
int main() { MyFunc myFunc; Calculate_class claculate; //每次随机调用矩形或者梯形分割 srand((unsigned)time(NULL)); int method = rand() % 2; cout << claculate(myFunc.func_1, 0, 1, method) << endl << claculate(myFunc.func_2, 0, 1, method) << endl; // cout << Calculate_func(func_1, 0, 1) << endl // << Calculate_func(func_2, 0, 1); return 0; }
C++真的是我学过的最骚气也最让人着迷的编程语言
浙公网安备 33010602011771号