浅谈一般函数指针和类的非静态成员…

1,语法
2,应用场景
3,应用技巧
4,回调函数
参考《C++ Primer Plus》《Effective C++》和“博客:类的成员函数指针”

一、函数指针的一般原理
与数据项类似,函数也有地址。函数的地址是存储其机器语言代码的内存的起始地址。函数调用,实际上就是执行内存在以函数地址为起始点的这段机器码,故只要获取函数地址,即可执行函数调用。而函数指针就是这样一中指向某类特定函数的 的内存的起始地址的指针类型。可以编写将另一个函数的地址作为参数的函数,它运行在不同的时间传递不同的函数的地址,这意味着可以在不同的时间使用不同的函数,它在一定程度上可以替代“继承多态”的功能,参考 《Effective C++》item35
与类的非静态数据成员不同,类的成员函数不会在运行时为每个类的实例对象生成一份专有的 机器语言代码, 类的成员函数在内存中只有一份,即类的成员函数的地址也是唯一的。这也就解释了,取类的成员函数的地址时,只需要将类作为限定,而不需要知道类的实例对象。从内存角度看, 类的成员函数和一般函数是一样的,特别是类的静态成员函数。但是,类的成员函数为了防止与其他类的成员函数名称冲突,编译器会添加一些额外的符号进行区分,故需要由类名进行引导搜素。

二、一般函数指针的用法
一般函数指针指的是除“ 类的非静态成员函数指针 ”之外的函数指针,它没有类的限定,只需要匹配函数签名(signature)和返回值即可。参考“ 博客:一般函数指针和类的成员函数指针 ”。在使用上有两种简化方式,一种是使用auto,另一种是使用typedef。如例:
已知函数:
int Func(int a, int b);
可定义函数指针(方式一):
auto pFunc = &Func;
方式二:
typedef int(* pFunc  )(int, int);
pFunc pfFunc = &Func;
第二种方式适合反复使用和作为参数。

三、类的非静态成员函数指针的用法
类的静态成员函数与一般函数类似,其函数指针用法参考上一条,故以下将类的非静态成员函数简称类的成员函数。而类的成员函数指针在使用的时候,需要使用类名进行限定。
参考“ 博客:一般函数指针和类的成员函数指针 ”:
class CA;
typedef int(CA::*pClassFunc)(int ,int);
注意域运算符和指针标示符的顺序。此外,一般可以将该typedef放在类的构造函数附近进行声明,限定其作用域。
int Resule(CA* pA, pClassFunc pfFunc, int a, int b)
{
    return (pA->*pClassFunc)(a, b);
}
注意函数调用符和解引用符的顺序。

四、函数指针应用
1,增加接口的弹性,提高代码复用。示例(来自于 《C++ Primer Plus》)
假设要设计一个名为estimate()的函数,估算编写指定行数的代码所需的时间,并且希望不同的程序员都将使用该函数。对于所有用户来说,estimate()中的一部分代码都是相同的 ,但该函数运行每个程序员提供自己的算法来估算时间。为实现这种目标,采用的机制是,将程序要使用的算法函数的地址传递给esimate()。
// fun_ptr.cpp -- pointer to functions
#include
double betsy(int);
double pam(int);

void estimate(int lines, double (*pf)(int));

int main()
{
    using namespace std;
    int code;
    cout << "How many lines of code you need ? ";
    cin >> code;
    cout << "Here's Betsy's estimate: \n";
    estimate(code,betsy);
    cout << "Here's Pam's estimate: \n";
    estimate(code, pam);
    cin.get();
    return 0;
}

double betsy(int lns)
{
    return 0.05 *lns;
}

double pam(int lns)
{
    return 0.03 * lns + 0.0004 * lns * lns;
}

void estimate(int lines, double (*pf)(int))
{
    using namespace std;
    cout << lines << " lines will take ";
    cout << (*pf)(lines) << " hour(s)\n";
}
附:通过这种设计,可以复用estimate()函数中除算法外的其他部分。如果将estimate作为接口,它将具有较强的弹性,用户可以根据自己的需要自由选择需要的函数传递给它。特别的,用户在不修改该接口(estimate())的情况下,可以自己实现一个算法函数,传递给该接口,实现不同个的计算效果。
STL的算法函数,传递给functor参数的设计方式也是基于这种设计模式。如自定义排序方式,参考 《C++ Primer Plus》P681。

2,替代虚函数机制实现多态, 参考 《Effective C++》item35 。它实际上也是Strategy设计模式的一种应用。(策略与机制相分离)示例如下:
计算游戏任务的健康指数
class GameCharacter;    // 前置声明
// 以下函数是计算健康指数的缺省算法
int defaultHealthCalc(const GameCharacter& gc);
class GameCharacter
{
    public:
        typedef int (*HealthCalcFunc)( const GameCharacter& );
        explict  GameCharacter(HealthCalcFunc hcf =  defaultHealthCalc)
          :healthFunc(hcf)
        {}
        int healthValue() const { return  healthFunc(*this); }
        ...
    private:
         HealthCalcFunc  healthFunc;        // 类成员是一个函数指针
}

五,函数指针用于回调
参考Qt中的“ qInstallMessageHandler”的用法






你可能感兴趣的:(浅谈一般函数指针和类的非静态成员…)