首页 > 技术文章 > C++中的函数指针

hickey2048 2021-07-23 22:44 原文

引言

函数指针指向的是函数而不是对象。函数指针指向某种特定类型,就像我们可以定义指向int变量的指针,指向char变量的指针以及指向string变量的指针,函数指针指向的这种特定类型由函数的返回类型形参类型共同决定,而与函数名无关。

例如有一个函数compare声明如下:

    bool compare(const string &, const string &);

若要定义指向该函数的指针,那么应该像这样:

    bool (*pf)(const string &,const string &);

依然遵循由内而外的原则阅读声明:

  • 首先(*pf)表明变量pf是指针类型;
  • 而后面的(const string &,const string &)是形参列表,表明指针pf指向一个函数,该函数的形参列表如上;
  • 最后,最左边的bool表明这个函数的返回类型是bool

Note:

    bool *pf(const string &,const string &);

对比上面的这个声明和上上个声明,差别在(*pf)*pf上。(*pf)指明pf是指针类型,而*pf对应的那条声明指明pf是个函数,函数的返回类型是bool *。因此,声明指向函数的指针时,(*pf)两端的括号必不可少,至于差别大家也看到了。

使用函数指针

当我们把函数名作为一个值使用时,该函数自动转换成指针
例如对上面的compare函数和函数指针pf,若要使pf指向compare函数,我们有:

    pf = compare;  //函数名自动转换为指针
    pf = &compare; //等价的赋值,&可选。

    pf = 0;  //表明pf不指向任何函数
    pf = nullptr;   //与上句等价

并且,我们能够直接使用指向函数的指针调用该函数,不一定非要解引用指针。例如:

    bool b1 = pf("你好", "Fuck!");  //直接使用指向函数的指针调用函数
    bool b2 = (*pf)("你好", "Fuck!"); //等价的调用。先解引用指针,再调用函数。
    bool b2 = *pf("你好", "Fuck!"); //错误的调用。函数调用运算符()的优先级高于解引用运算符*的优先级。
    /*
        bool b2 = *pf("你好", "Fuck!");//等价于以下两条语句:

        bool tmp = pf("你好", "Fuck!"); //即调用pf指向的函数。
        bool b2 = *tmp; //对tmp解引用。显然错误,因为不能对bool类型解引用
        //如果compare函数返回char *类型,则语句bool b2 = *pf("你好", "Fuck!");反而正确
    */
    bool b3 = compare("你好", "Fuck!"); ////等价的调用。直接使用函数。

我们再来加深下对函数指针指向的类型的理解:
假设有三个函数声明如下:

    bool compare1(const string &, const string &);
    bool compare2(const string &, const string &);
    bool compare3(const int &, const int &);

函数指针pf如下:

    bool (*pf) (const string &, const string &);
    pf = compare1;  //正确
    pf = compare2;  //正确
    pf = compare3;  //错误,形参不匹配

(重载函数)的指针

考虑重载的函数:

    void f(int *);
    void f(int);

    void (*pf) (int) = f; //pf指向void f(int);

编译器通过指针类型决定指向哪个重载函数,指针类型必须与重载函数中的某一个精确匹配(返回类型和形参列表精确匹配)。

将函数指针作为形参

就如我们不能定义数组类型的形参一样,我们不能定义函数类型的形参,但是形参可以是指向函数的指针。形参看起来是函数类型,实际上当成指针使用。
例如:

    //第三个形参是函数类型,但会自动转换为指向函数的指针。即,pf是指向函数的指针。
    void f(const string &s1, const string &s2, bool pf(const string &, const string &));

    //等价的声明,显式地将形参定义为指向函数的指针。
    void f(const string &s1, const string &s2, bool (*pf)(const string &, const string &));

直接把函数作为实参时,会自动转换为指针。

    //自动将函数compare转换为指向该函数的指针。
    f("你好", "Fuck", compare);

将指向函数的指针作为返回类型

就如我们不能在函数中返回数组类型一样,我们不能直接返回函数类型,但可以返回指向函数的指针。以下声明一个返回函数指针的函数:

    int (*f(int)) (int *, int);

从内而外阅读声明,首先*f(int)表明f是函数,参数是int ,返回指针类型。(int *, int)表明指针指向函数一个,且参数列表为(int *, int),最左边的int表明指向的该函数返回int

亦可以使用尾置返回类型的方式:

    auto f(int) -> int (*) (int*, int);

该声明与上一条声明等价。

将decltype用于函数指针类型

如果我们明确知道返回的函数是哪一个,就能使用decltype简化书写函数指针。

    decltype(compare) *p; //p是一个指向compare函数对应类型(只是返回类型和形参,函数名不属于类型的一部分)的指针类型
    decltype(compare) * f(); //函数声明,f是一个函数,没有形参,返回类型为指向compare函数对应类型(只是返回类型和形参,函数名不属于类型的一部分)的函数指针类型

Note:

decltype作用于某个函数时,返回函数类型而不是指针类型,需要显式加上*表明我们需要指针类型。


声明:
c++ Basic是对《C++ Primer 第五版》的个人总结与疑难解释,主要用于个人日后复习。
如果想要深入了解更多,请支持正版。

推荐阅读