|
马上注册,结交更多好友,享用更多功能^_^
您需要 登录 才可以下载或查看,没有账号?立即注册
x
本帖最后由 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[i])();
- 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[10];
- int age;
- bool sex;
- int sID;
- };
- typedef void(Student::*pStuFun)(); // 定义指向成员函数的函数指针类型,可放在类中
- void Student::printMessage()
- { // 函数指针数组 注意必须是 &类名::成员函数名 {注意:中间不能加括号 &(类名::成员函数名)}
- pStuFun psf1[4] = { &Student::getnName, &Student::getAge, &Student::getSex, &Student::getsID };
- for (int i = 0; i < 4; i++)
- {
- (*this.*psf1[i])(); //或 (this->*psf1[i])();
- //错误写法
- //*this.*psf1[i]();
- //*this.(*psf1[i])();
- //(*this.(*psf1[i]))();
- }
- }
- int main()
- {
- Student s1("小明", 23, 1, 1802);
- s1.printMessage();
- system("pause");
- return 0;
- }
复制代码
结论5:动态联编、多态的深入理解
所谓的动态联编:就是说函数在运行时才确定函数地址!(是通过函数指针来实现的); 也就是在调用成员函数通过 (对象.*函数指针)(形参).
多态是C++编译器通过vptr指针去查找虚函数表然后隐式帮我们完成了这个过程。
本小节参考博文。 |
|