我们要做的是:将一个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