鱼C论坛

 找回密码
 立即注册
查看: 2482|回复: 0

[学习笔记] 038-C/C++之函数类型和函数指针

[复制链接]
发表于 2018-9-9 20:35:49 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能^_^

您需要 登录 才可以下载或查看,没有账号?立即注册

x
本帖最后由 moc 于 2018-9-12 20:11 编辑

1、函数类型
        在C语言中,函数也是一种类型,同样也可以定义指向函数类型的指针。
函数三要素:名称、参数、返回值。
其中:函数的名称就代表函数的入口地址;  函数名本身就是一个指针。
与数组类型相仿,也可以自定义函数类型:
        typedef type name(parameter list);
  1. int test(int a)
  2. {
  3.         cout << a*a << endl;
  4.         return a*a;
  5. }

  6. typedef int Func(int);     // 定义一个函数类型
  7. Func *myfun = NULL;   // 用函数类型  定义一个函数指针
  8. myfun = test;     // 函数指针指向一个函数
  9. myfun(2);          //通过函数指针调用函数

  10. myfun = *(*test);
  11. myfun = &(&(&test));
复制代码

扩展:对函数名取多少次地址或*p操作,都是一样的,其结果都是他本身。
2、函数指针
除了通过上面的函数类型定义函数指针外,还可以通过定义函数指针类型来直接定义函数指针。
函数指针类型:
        typedef type(*name)(parameter list);
也可直接定义函数指针:
        type (*pointer)(parameter list);
  1. typedef int(*MyPFun)(int);  // 定义一个指向函数类型的指针类型
  2. MyPFun aa = test;           // 通过函数指针类型定义函数指针
  3. aa(10);    // 调用函数
  4. int(*myf1)(int) = f;  //直接定义一个函数指针并且赋值
  5. myf1(20);
复制代码

3、函数指针做函数参数
  1. #include "stdio.h"
  2. #include "stdlib.h"
  3. #include "string.h"

  4. int add(int a, int b) { return a + b; }

  5. // pDis是add的入口地址;  函数指针做函数参数
  6. int libfun(int(*pDis)(int a, int b))
  7. {
  8.         int a, b;
  9.         a = 1;
  10.         b = 2;
  11.         printf("%d\n", pDis(a, b));   // 执行了add的调用
  12. }

  13. int main()
  14. {
  15.         int(*pfun)(int a, int b);     // 直接定义了一个函数指针
  16.         pfun = add;           // 函数名赋给函数指针,即把函数入口地址赋给pfun
  17.         libfun(pfun);         //  函数指针做函数实参

  18.         system("pause");
  19.         return 0;
  20. }
复制代码

像上面函数指针做函数参数,如果放在一个函数中,没有任何意义,函数指针主要有两种应用场景:
        ① 正向调用    用于动态库加载
        ② 反向调用    用于回调函数
4、正向调用
动态库的加载有两种方式,除了前面的通过编译器加载(把lib文件放入链接器的输入的附加依赖项)中,还可以通过函数指针手动的加载。。
难点:
        理解被调用函数是什么机制被调用起来的。   --->框架。
        框架提前设置了被调用函数的入口(框架提供了第三方模块入口的集成功能)。
        框架具备调用第三方模块的入口函数。
MFC演示手工动态库加载:
  1. typedef int(*CltSocketInit)(void *handle);  // 定义函数指针类型
  2. HINSTANCE hIstance = NULL;
  3. hIstance = ::LoadLibrary(TEXT("d:/socketclient.dll"));   // MFC框架提供加载动态库的函数LoadLibrary
  4. // 从动态库取得函数的入口地址赋给函数指针
  5. CltSocketInit cltSocketInit = (CltSocketInit)::GetProAddress(hIstance, "cltSocketInit");  // MFC框架通过函数名找到动态库里的函数的入口地址。

  6. void *handle = NULL;
  7. 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])();
  1. class Student
  2. {
  3. public:
  4.         Student(char* buf, int age, bool sex, int sID)
  5.         {
  6.                 strcpy(name, buf);
  7.                 this->age = age;
  8.                 this->sex = sex;
  9.                 this->sID = sID;
  10.         }
  11.         void getnName() { cout << name << endl; }
  12.         void getAge() { cout << age << endl; }
  13.         void getSex() { cout << sex << endl; }
  14.         void getsID() { cout << sID << endl; }
  15.         void printMessage();

  16. private:
  17.         char name[10];
  18.         int age;
  19.         bool sex;
  20.         int sID;
  21. };

  22. typedef void(Student::*pStuFun)();  // 定义指向成员函数的函数指针类型,可放在类中
  23. void Student::printMessage()
  24. {   // 函数指针数组   注意必须是  &类名::成员函数名  {注意:中间不能加括号 &(类名::成员函数名)}
  25.         pStuFun psf1[4] = { &Student::getnName, &Student::getAge, &Student::getSex, &Student::getsID };
  26.         for (int i = 0; i < 4; i++)
  27.         {
  28.                 (*this.*psf1[i])();   //或 (this->*psf1[i])();

  29.                 //错误写法
  30.                 //*this.*psf1[i]();
  31.                 //*this.(*psf1[i])();
  32.                 //(*this.(*psf1[i]))();
  33.         }
  34. }

  35. int main()
  36. {
  37.         Student s1("小明", 23, 1, 1802);
  38.         s1.printMessage();

  39.         system("pause");
  40.         return 0;
  41. }
复制代码

结论5:动态联编、多态的深入理解
        所谓的动态联编:就是说函数在运行时才确定函数地址!(是通过函数指针来实现的); 也就是在调用成员函数通过    (对象.*函数指针)(形参).
        多态是C++编译器通过vptr指针去查找虚函数表然后隐式帮我们完成了这个过程。
本小节参考博文

本帖被以下淘专辑推荐:

小甲鱼最新课程 -> https://ilovefishc.com
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Archiver|鱼C工作室 ( 粤ICP备18085999号-1 | 粤公网安备 44051102000585号)

GMT+8, 2025-6-22 07:04

Powered by Discuz! X3.4

© 2001-2023 Discuz! Team.

快速回复 返回顶部 返回列表