思维导图

函数指针 - 图1

1. 声明函数指针

函数指针指向的是函数而非对象。和其他指针一样,函数指针指向某种特定类型。函数的类型有它的返回类型和形参类型共同决定,与函数名无关。
bool (*pf)(const string &, int); // 未初始化
pf前面有个*,因此pf是指针;右侧是形参列表(此处有两个形参,一个是const string &型,另一个是int型),表示pf指向的是函数;左侧为返回类型(此处为bool型)。
注意:*pf两端的括号必不可少。如果不写这对括号,则pf是一个返回值为bool指针的函数。

2. 使用函数指针

当我们把函数名作为一个值使用时,该函数自动地转化为指针。

  1. pf = checkLenth; // pf指向名为checkLenth的函数
  2. pf = &checkLenth; // 等价的赋值语句:取地址符是可选的

可以直接使用指向函数的指针调用该函数,无须提前解引用指针。

  1. bool b1 = pf(str, lenth); // 调用checkLenth函数
  2. bool b2 = (*pf)(str, lenth); // 等价的调用
  3. bool b3 = checkLenth(str, lenth); // 等价的调用

在指向不同函数类型的指针间不存在转化规则。通常可以为函数指针赋一个nullptr或者值为0的整型常量表达式,表示该指针没有指向任何一个函数。

  1. /************************main.cpp************************/
  2. #include <iostream>
  3. #include <string>
  4. using std::string;
  5. bool checkLenth(const string &str, int lenth)
  6. {
  7. if (str.length() != lenth)
  8. {
  9. return false;
  10. }
  11. return true;
  12. }
  13. bool checkStr(const string &str1, const string &str2)
  14. {
  15. if (str1.length() != str2.length())
  16. {
  17. return false;
  18. }
  19. return true;
  20. }
  21. int main(int argc, char *argv[])
  22. {
  23. bool(*pf1) (const string &, int); // 声明一个函数指针,未初始化
  24. bool(*pf2) (const string &, int) = nullptr; // 声明一个函数指针,并初始化,表示未指向任何一个函数
  25. string str = "hello world!";
  26. int lenth = 12;
  27. pf1 = checkLenth;
  28. // pf1 = &checkLenth; // 等价的赋值语句:取地址符是可选的
  29. // pf2 = checkStr; // 错误,形参类型不匹配,不同函数类型的指针间不存在转换规则
  30. bool b1 = pf1(str, lenth); // 调用checkLenth函数
  31. bool b2 = (*pf1)(str, lenth); // 等价的调用
  32. bool b3 = checkLenth(str, lenth); // 等价的调用
  33. system("pause");
  34. }

3. 重载函数的指针

当使用重载函数时,上下文必须清晰地界定到底应该选用那个函数。

  1. /************************main.cpp************************/
  2. #include <iostream>
  3. #include <string>
  4. using std::string;
  5. bool checkStr(const string &str1, const string &str2)
  6. {
  7. if (str1.length() != str2.length())
  8. {
  9. return false;
  10. }
  11. return true;
  12. }
  13. // 重载函数
  14. bool checkStr(const string &str1, const string &str2, int lenth)
  15. {
  16. if (str1.length() != lenth
  17. || str2.length() != lenth)
  18. {
  19. return false;
  20. }
  21. return true;
  22. }
  23. int main(int argc, char *argv[])
  24. {
  25. using std::cout;
  26. using std::endl;
  27. bool(*pf1) (const string &, int) = nullptr; // 声明一个函数指针
  28. void(*pf2) (const string &, const string &) = nullptr; // 声明一个函数指针
  29. bool(*pf3) (const string &, const string &, int) = nullptr; // 声明一个函数指针
  30. string str1 = "hello world!";
  31. string str2 = "hell0 c++pri";
  32. int lenth = 11;
  33. // pf1 = checkStr; // 错误,没有任何一个checkStr与该形参列表匹配
  34. // pf2 = checkStr; // 错误,checkStr与pf2的返回类型不匹配
  35. pf3 = checkStr; // 正确
  36. bool b3 = pf3(str1, str2, lenth);
  37. system("pause");
  38. }

4. 函数指针形参

和数组类似,虽然不能定义函数类型的形参,但是形参可以是指向函数的指针。
使用typedefdecltype可以简化使用函数指针。

  1. //#include "mainwindow.h"
  2. //#include <QtWidgets/QApplication>
  3. #include <iostream>
  4. #include <string>
  5. #ifndef SAFE_DELETE
  6. #define SAFE_DELETE(p) { if(p != nullptr){delete(p); (p) = nullptr;} }
  7. #endif
  8. using std::string;
  9. bool checkStr(const string &str1, const string &str2)
  10. {
  11. if (str1 > str2)
  12. {
  13. return true;
  14. }
  15. return false;
  16. }
  17. // Func和Func2是函数类型
  18. typedef bool Func(const string &, const string &);
  19. typedef decltype(checkStr) Func2;
  20. // FuncP和FuncP2是函数类型
  21. typedef bool (*FuncP)(const string &, const string &);
  22. typedef decltype(checkStr) *FuncP2;
  23. // 第三个形参是函数类型,它会自动地转换成指向函数的指针
  24. string useBigger(const string &str1, const string &str2, bool pf(const string &, const string &))
  25. {
  26. if (pf(str1, str2))
  27. {
  28. return str1;
  29. }
  30. else
  31. {
  32. return str2;
  33. }
  34. }
  35. // 等价的声明,采用类型别名(typedef)和decltype简化函数类型
  36. // string useBigger(const string &str1, const string &str2, Func pf);
  37. // string useBigger(const string &str1, const string &str2, Func2 pf);
  38. // 等价的声明:显示地将形参定义成指向函数的指针
  39. // string useBigger(const string &str1, const string &str2, bool (*pf)(const string &, const string &));
  40. // 等价的声明,采用类型别名(typedef)和decltype简化函数指针类型
  41. // string useBigger(const string &str1, const string &str2, FuncP pf);
  42. // string useBigger(const string &str1, const string &str2, FuncP2 pf);
  43. int main(int argc, char *argv[])
  44. {
  45. bool(*pf) (const string &, const string &) = checkStr; // 声明一个函数指针
  46. string str1 = "ac";
  47. string str2 = "ab";
  48. string retStr = useBigger(str1, str2, checkStr); // 自动将checkStr转换成指向该函数的指针
  49. // string retStr = useBigger(str1, str2, &checkStr); // 等价的调用,显示的调用函数指针
  50. // string retStr = useBigger(str1, str2, pf); // 等价的调用
  51. // string retStr = useBigger(str1, str2, *pf); // 等价的调用
  52. system("pause");
  53. }

注意:decltype返回函数类型,此时不会将函数类型自动转换成指针类型,所以在typedef decltype(checkStr) *FuncP2中在FuncP2前加了一个*,表示checkStr函数的指针。

5. 返回指向函数的指针

和数组类似,虽然不能返回一个函数,但是能返回指向函数类型的指针。
我们必须把返回类型写成指针形式,编译器不会自动地将函数返回类型当成对应的指针类型处理。

// 直接声明
int (*f1(int))(int *, int);

// 使用别名
using F = int(int*, int); // F是函数类型,不是指针
using PF = int(*)(int *, int); // PF是指针类型

// 尾置返回类型方式
auto f1(int)->int(*)(int *, int);

对于直接声明,按照由内向外的顺序阅读这条语句,f1形参列表(此处为int),所以f1是个函数f1前面有*,所以f1返回一个指针指针类型本身也包含形参列表(此处为int*int),因此指针指向函数,该函数返回类型是int
和函数类型的形参不一样,返回类型不会自动地转换成指针,必须显示地将返回类型指定为指针。

PF f1(int); // 正确:PF是指向函数的指针,f1返回指向函数的指针
F f1(int);    // 错误:F是函数类型,f1不能返回一个函数
F *f1(int);    // 正确:显示地指定返回类型是指向函数的指针

将decltype用于函数指针类型

// 申明两个函数
string::size_type sumLenth(const string &, const string &);
string::size_type largeLenth(const string &, const string &);
// 根据其形参的取值,getFcn函数返回指向sumLenth或者largeLenth的指针
decltype (sumLenth) *getFcn(const string &);

注意:decltype作用于某个函数时,它返回函数类型,而非指针类型。

#include <iostream>
#include <string>

using std::string;

string::size_type sumLenth(const string &str1, const string &str2)
{
    return str1.length() + str2.length();
}

string::size_type largeLenth(const string &str1, const string &str2)
{
    string::size_type len1 = str1.length();
    string::size_type len2 = str2.length();

    if (len1 > len2)
    {
        return len1;
    }
    else
    {
        return len2;
    }
}

// 采用别名方式
using F = string::size_type(const string &, const string &); // F是函数类型,不是指针
using PF = string::size_type(*)(const string &, const string &); // PF是指针类型

F *getFcn(const string &type) // 显式地指定返回类型是真相函数的指针
// PF getFcn(const string &type) // 等价的写法
{
    if (type == "1")
    {
        return sumLenth;
    }
    else
    {
        return &largeLenth;
    }
}

// 直接申明方式,此处直接定义
string::size_type(*f1(const string &type))(const string &, const string &)
{
    if (type == "1")
    {
        return sumLenth;
    }
    else
    {
        return &largeLenth;
    }
}

// 尾置返回类型声明f2,此处直接定义
auto f2(const string & type)->string::size_type(*)(const string &, const string &)
{
    if (type == "1")
    {
        return sumLenth;
    }
    else
    {
        return &largeLenth;
    }
}

int main(int argc, char *argv[])
{
    string str1 = "hello";
    string str2 = "world!";

    F *pf1 = getFcn("1"); // 显式地指定指针类型
    // PF pf1 = getFcn("1"); // 等价的调用
    // PF pf1 = f1("1"); // 等价的调用,采用别名方式
    string::size_type size1 = pf1(str1, str2); // 此处size1 = 11
    string::size_type size1_1 = f1("1")(str1, str2); // 直接调用方式,此处size1_1 = 11

    F *pf2 = getFcn("2"); // 显式地指定指针类型
    //PF pf2 = getFcn("2"); // 等价的调用
    string::size_type size2 = pf2(str1, str2); // 此处size1 = 6
    string::size_type size2_1 = f2("2")(str1, str2); // 直接调用方式,此处size1_1 = 6

    system("pause");
}

6. 成员函数指针

详见类成员指针章节的成员函数指针

参考:

1)《C++ Primer 中文版(第5版)》;
2)C/C++ 函数指针使用总结