038-C/C++之函数类型和函数指针
本帖最后由 moc 于 2018-9-12 20:11 编辑1、函数类型
在C语言中,函数也是一种类型,同样也可以定义指向函数类型的指针。
函数三要素:名称、参数、返回值。
其中:函数的名称就代表函数的入口地址;函数名本身就是一个指针。
与数组类型相仿,也可以自定义函数类型:
typedef type name(parameter list);
int test(int a)
{
cout << a*a << endl;
return a*a;
}
typedef int Func(int); // 定义一个函数类型
Func *myfun = NULL; // 用函数类型定义一个函数指针
myfun = test; // 函数指针指向一个函数
myfun(2); //通过函数指针调用函数
myfun = *(*test);
myfun = &(&(&test));
扩展:对函数名取多少次地址或*p操作,都是一样的,其结果都是他本身。
2、函数指针
除了通过上面的函数类型定义函数指针外,还可以通过定义函数指针类型来直接定义函数指针。
函数指针类型:
typedef type(*name)(parameter list);
也可直接定义函数指针:
type (*pointer)(parameter list);
typedef int(*MyPFun)(int);// 定义一个指向函数类型的指针类型
MyPFun aa = test; // 通过函数指针类型定义函数指针
aa(10); // 调用函数
int(*myf1)(int) = f;//直接定义一个函数指针并且赋值
myf1(20);
3、函数指针做函数参数
#include "stdio.h"
#include "stdlib.h"
#include "string.h"
int add(int a, int b) { return a + b; }
// pDis是add的入口地址;函数指针做函数参数
int libfun(int(*pDis)(int a, int b))
{
int a, b;
a = 1;
b = 2;
printf("%d\n", pDis(a, b)); // 执行了add的调用
}
int main()
{
int(*pfun)(int a, int b); // 直接定义了一个函数指针
pfun = add; // 函数名赋给函数指针,即把函数入口地址赋给pfun
libfun(pfun); //函数指针做函数实参
system("pause");
return 0;
}
像上面函数指针做函数参数,如果放在一个函数中,没有任何意义,函数指针主要有两种应用场景:
① 正向调用 用于动态库加载
② 反向调用 用于回调函数
4、正向调用
动态库的加载有两种方式,除了前面的通过编译器加载(把lib文件放入链接器的输入的附加依赖项)中,还可以通过函数指针手动的加载。。
难点:
理解被调用函数是什么机制被调用起来的。 --->框架。
框架提前设置了被调用函数的入口(框架提供了第三方模块入口的集成功能)。
框架具备调用第三方模块的入口函数。
MFC演示手工动态库加载:
typedef int(*CltSocketInit)(void *handle);// 定义函数指针类型
HINSTANCE hIstance = NULL;
hIstance = ::LoadLibrary(TEXT("d:/socketclient.dll")); // MFC框架提供加载动态库的函数LoadLibrary
// 从动态库取得函数的入口地址赋给函数指针
CltSocketInit cltSocketInit = (CltSocketInit)::GetProAddress(hIstance, "cltSocketInit");// MFC框架通过函数名找到动态库里的函数的入口地址。
void *handle = NULL;
int ret = cltSocketInit(handle); // 调用动态库里的函数
5、反向调用
回调机制的原理:
① 当具体事件发生时,调用者通过函数指针调用具体函数;
② 回调机制将调用者和被调函数分开,两者互不依赖;
③ 任务的实现 和 任务的调用可以 解耦合 (提前进行接口的封装和设计)。
如何把一个普通的动态库变成一个业务模型框架:
1. 把第三方的业务入口传入框架(dll)(传入回调函数的入口地址)
传入的方式有两种:
① 回调函数的入口地址缓存到框架(dll)中;
② 回调函数入口(函数名)直接放入框架函数参数中。
2. 实现动态库 和 业务模型的抽象
6、指向成员函数的函数指针
结论1: Teacher::printA 和 &Teacher::printA 虽然在值上是一样的,都表示成员函数的入口地址,但是不加&属于不规范操作,在给指向成员函数的函数指针赋值时会报错。
结论2: ps1 = &Teacher::printA;(ps1为函数指针)指向成员函数的函数指针正确的赋值操作,类的成员函数加括号取地址再给函数指针属于非法操作(Vs2015下);
结论3:自定义类型、类也有作用域,只在自己的命名空间下有效。
结论4:指向类的成员函数通过函数指针的正确调用方法:(*this.*psf1)();
class Student
{
public:
Student(char* buf, int age, bool sex, int sID)
{
strcpy(name, buf);
this->age = age;
this->sex = sex;
this->sID = sID;
}
void getnName() { cout << name << endl; }
void getAge() { cout << age << endl; }
void getSex() { cout << sex << endl; }
void getsID() { cout << sID << endl; }
void printMessage();
private:
char name;
int age;
bool sex;
int sID;
};
typedef void(Student::*pStuFun)();// 定义指向成员函数的函数指针类型,可放在类中
void Student::printMessage()
{ // 函数指针数组 注意必须是&类名::成员函数名{注意:中间不能加括号 &(类名::成员函数名)}
pStuFun psf1 = { &Student::getnName, &Student::getAge, &Student::getSex, &Student::getsID };
for (int i = 0; i < 4; i++)
{
(*this.*psf1)(); //或 (this->*psf1)();
//错误写法
//*this.*psf1();
//*this.(*psf1)();
//(*this.(*psf1))();
}
}
int main()
{
Student s1("小明", 23, 1, 1802);
s1.printMessage();
system("pause");
return 0;
}
结论5:动态联编、多态的深入理解
所谓的动态联编:就是说函数在运行时才确定函数地址!(是通过函数指针来实现的); 也就是在调用成员函数通过 (对象.*函数指针)(形参).
多态是C++编译器通过vptr指针去查找虚函数表然后隐式帮我们完成了这个过程。
本小节参考博文。
页:
[1]