函数指针在全局函数和在类内函数使用的区别

我们要做的是:将一个add函数作为另一个process函数的参数, 并在process函数里调用add;这个我们分别在全局函数中实现和在类中实现,看看两者有什么区别。

函数指针

先联想下,什么是函数指针和什么时候我们需要用到函数指针?
我的第一反应,函数指针,就是指向函数的指针,类比int* p //p是指向int的指针,那么函数指针 它的类型是什么,它应该怎么定义;
这时候我们想想什么时候用到函数指针,我的第一反应两个地方:
a. dll 动态库加载的时候 b. 回调函数的时候

函数指针 它的类型是什么,它应该怎么定义? 带着这个问题,先回顾下dll 动态库加载的时候,我们怎么用的?

假设一个动态库dll, 里面封装了一个方法add,我们需要再另一个函数里调用它

// add.dll
extern "C" _declspec(dllexport) int __stdcall add(int a, int b)
{
    return a+b;
}
// 另一个进程 main.exe
// 需要定义一个函数指针
typedef int (*ADDPROC)(int a, int b);
// ADDPROC相当于函数指针的类型 类比int* p; ADDPROC相当于int*
ADDPROC addproc; // 定义一个函数指针变量
// 需要加载dll,这里采用动态加载
HINSTANCE hDll = LoadLibary("add.dll");
if(nullptr != hDll)
{
    addproc = (ADDPROC)GetProcAddress(hDll, "add");
}
else
{
    // 加载dll失败
    ...
}

上面是一个加载dll的简单示例,我们发现,动态加载dll,在获取一个函数地址的时候,我们用到了函数指针
ADDPROC它是一个类型,代表着这是一个add函数类型的指针类型,int*, 用它可以定义一个变量,ADDPROC addproc;
这里参考https://www.cnblogs.com/AnnieKim/archive/2011/11/20/2255813.html,给出函数指针的定义赋值的总结
1) 函数指针的初始化。

//函数如下:
int CompareString(const string& str1, const string& str2)
{
   return str1.compare(str2);  
}

函数指针的初始化有两种方式:

第一种,也是最普遍的方式:

int (*CompareFunction)(const string&, const string&) = CompareString;

第二种,是使用typedef定义函数类型,这种写法有助于对代码的理解:

typedef int (*CompareFunctionType)(const string&, const string&);
CompareFunctionType CompareFunction = CompareString;

2) 函数指针赋值。
函数名可以理解为该类型函数的指针。当然,取地址操作符作用于函数名上也能产生指向该类型函数的指针。也就是说下面两种赋值都是可行的:

CompareFunctionType CompareFunction = CompareString;
CompareFunctionType CompareFunction = &CompareString;

在全局函数中使用函数指针

已经知道函数指针定义和初始化,那么我们想要在全局函数中;将add函数作为参数传递给process函数,如下

int add(int a, int b)
{
    return a+b;
}
typedef int (*ADDPROC)(int a, int b);
// ADDPROC addproc
int process(ADDPROC addproc, int a, int b)
{
    addproc(a,b); // 直接调用add函数
}

int main()
{
    ADDPROC addproc = add; // 定义一个add指针类型的变量,并赋值为add函数的地址,也可以写成ADDPROC addproc = &add
    int res = addproc(2,1); // res=3 可以通过定义好的函数指针来访问函数 
    res = process(addproc, 1, 4); //res = 5  也可以把函数指针当成参数传入 
    res = process(add, 5, 4);  // res=9 这里也可以直接传add这个函数名进去 
    return 0;
}

这里我们就已经完成了全局函数里,将函数作为变量传递给另一个函数;
那如果放在类中,应该怎么做呢?

在类中使用函数指针

这里实现的的,类内需要用到函数指针,调用类的成员函数add,这种类内使用函数指针,则add函数(函数指针指向的函数)应该为静态成员函数
我们先把这种全局函数版的直接搬到类里,看看会报什么错

class Solution {
public:
    // 非静态函数
    int add(int a, int b)
    {
        return a+b;
    }
    typedef int (*ADDPROC)(int a, int b);// 声明一个函数指针类型
    int process(ADDPROC addproc, int a, int b)
    {
        return addproc(a,b); // 直接调用add函数
    }
    // 类里调用
    int test()
    {
        //error: reference to non-static member function must be called
        process(Solution::add, 3, 4);
        return 0;
    }
};

int main()
{
    Solution sol;
    //error: reference to non-static member function must be called
    sol.process(sol.add, 3, 4); // 类外调用
    return 0;
}

测试后发现,无论是类里面调用还是类外调用,编译都会报错;报错信息,跟静态成员函数相关;
我们把add函数挪到类外作为全局函数,或者把add声明成静态成员函数,则可以通过编译;为什么?
这是因为非静态的成员函数还多了一个隐式this指针参数(implicit parameter)
int add(Solution* this, int a, int b) // 普通成员函数的全部形参
这时,把add函数放在类外或者加上static声明,则没有问题;
说明静态成员函数和类实例没有关系,所有的类是共享的同一个static成员函数;
修改后的代码如下:

typedef int (*ADDPROC)(int a, int b); // 函数指针类型定义写在类里类外都可以

class Solution {
public:
    // 非静态函数版
    static int add(int a, int b)
    {
        return a+b;
    }

    int process(ADDPROC addproc, int a, int b)
    {
        return addproc(a,b); // 直接调用add函数
    }

    ADDPROC _addproc;
};

int main()
{
    ADDPROC addproc = &Solution::add;
    Solution sol;
    int res = sol.process(sol.add, 3, 4); // 直接通过函数名当参数
    res = sol.process(addproc, 3, 4); // 通过局部变量函数指针当参数
    res = addproc(2,4); // 相当于调用sol.add(2,4)
    res = (*addproc)(7,8); // 严格遵守指针解引用到函数的方式,相当于调用sol.add(7,8)

    // 类的成员变量 函数指针
    sol._addproc = &sol.add; //  也可以写成这样:sol._addproc = sol.add;
    res = sol._addproc(1, 2);
    return 0;
}

这个链接里,是类外声明的函数指针类型,然后函数指针是全局的;函数指针指向类的静态成员函数和非静态成员函数都是可以的;本质上还是类外使用函数指针;
https://www.cnblogs.com/AnnieKim/archive/2011/12/04/2275589.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值