| 
 | 
 
马上注册,结交更多好友,享用更多功能^_^
您需要 登录 才可以下载或查看,没有账号?立即注册  
 
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指针去查找虚函数表然后隐式帮我们完成了这个过程。 
本小节参考博文。 |   
 
 
 
 |