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++真的是我学过的最骚气也最让人着迷的编程语言

 

posted @ 2020-03-31 22:30  寂寞灬突袭  阅读(355)  评论(0)    收藏  举报