在C++中,指针是一个核心的部分,常规的指针用法作者已经在另外一篇文章中有过详细介绍,这篇文章主要是针对函数指针进行讨论。

首先我们先来认识一下函数指针的概念,我们平时在写函数的时候,调用函数时总是以函数名+小括号的形式来进行调用,事实上,函数在内存中也有相应的存储位置,也就是函数也有地址,那么我们也可以通过地址来获取到函数,提到地址,我们自然而然的就会联想到指针,因为指针用来保存的就是地址,那么函数指针的用途也很清楚了,就是用来保存函数的地址。

首先我们来看一下如何声明一个函数指针:

void (*func_ptr)();

如上形式就声明了一个函数指针,那么这个指针指向怎么样的函数呢?事实上,这个指针指向的是一个返回值为void,并且参数列表为空的函数,那么,假设我现在我一个函数:

void func();
// 我可以用上面声明的指针指向这个函数
func_ptr = func;

这样就可以完成一个函数指针的赋值(Tips: 在C++中,函数名就是这个函数对应的地址,所以可以直接用指针=函数名这样的形式为指针赋值)。

和普通的指针一样,我们可以通过这个指针获取到指针所指向内容,如果我们想要通过这个指针调用这个函数,我们可以用如下操作:

func_ptr();

这样的形式就和我们调用普通的函数一样,是不是非常的简单。

接下来我们来进阶一下,上面的函数指针指向一个返回值为void,参数列表为空的函数,现在我们假设我们的函数指针需要指向一个返回值为一个整形的指针,参数列表有两个整形变量的函数,那么我们可以这样声明:

// 函数指针声明
int* (*func_ptr)(int , int);
// 现在有一个函数, 仅仅是声明,没有做出实现
int* func(int, int);
// 我们可以这样使用
func_ptr = func;
// 通过函数指针调用函数
func_ptr(1, 2);

这个例子也是比较简单,接下来我们来试下更难的,假设一个函数返回一个函数指针(是的,因为函数指针是一个指针,所以是可以作为参数返回的!)这个函数的参数只有一个,也是一个函数指针,接下来我们要来开始见证C++的魅力了。

我们先做出假设,这个函数返回的指针指向一个返回值为void,参数为空的函数,这个函数的参数的函数指针指向一个返回值为int,参数列表为int的一个函数

那么我们可以这样声明:

// 我们的目标
void(* (*func_ptr)( int(*)(int) ))();

ok,我相信大部分同学估计到了这里看不懂了,  其实可以不用懂这么复杂的声明,因为确实很复杂哈哈哈,不过我们在C++中可以使用typedef和using关键字定义别名,这样我们可以简化声明

首先是typedef:

// 先声明返回值的函数指针
typedef void(*ret)();
// 再声明参数需要的函数指针
typedef int(*arg)(int);
// 最后声明我们需要的函数指针
typedef ret (*aim) (arg);
// 这样做之后,aim就是一个具有此类型的函数指针,如果我们需要多个此类型的指针,我们可以多次声明
aim ptr1;
aim ptr2;
aim ptr3;
// ptr1, ptr2, ptr3 都和 void(* (*func_ptr)( int(*)(int) ))() 等价

接下来是使using关键字,值得一提的是,using关键字有这样的功能是在C++11之后,并且功能比typedef更加强大,完全可以代替typedef。

// 同理,先声明返回值的指针
using ret = void (*) ();
// 再声明参数需要的指针
using arg = int (*) (int);
// 最后声明我们的目标
using aim = ret (*) (arg);

以上就是函数指针相关的语法知识,接下来我们来讨论函数指针的应用。

如果有小伙伴接触过C++的GUI编程的话,比如Qt或者Opencv,都可以发现用到了大量的函数指针,这些函数指针的作用主要就是用于回调函数,比方说我现在有一个函数,参数是一个函数指针,这个时候我们可以再这个函数运行的过程中去通过函数指针去调用这个函数指针指向的函数,这个函数指针指向的函数我们就称作回调函数。

看下面这个例子:

#include <iostream>
using namespace std;// 先利用using重命名一个函数指针
using FUNC_PTR = void (*) ();
// 接下来声明两个函数
void show_age() {cout << 18 << endl;
}void show_name() {cout << "djs" << endl;
}// 最后一个函数,参数需要一个函数指针
void test(string msg, FUNC_PTR func_ptr) {cout << msg << " ";func_ptr(); // 回调
}// 在主函数中嗲调用测试
int main() {test("name:", show_name);test("age:", show_age);return 0;
}

以上程序的运行结果为:

我们分析以上程序,我们有两个函数,一个函数的功能是打印年龄,另外一个函数的功能是打印姓名,现在又如下需求,就是在打印的信息前分别加上对应的字段,比如姓名加上name,年龄加上age,如果我们没有使用test这个函数的话,我们只有两种做法,第一种就是分别去修改show_name和show_age中的代码,这样做确实可以,但是并不好,因为这里代码量很少,所以我们修改起来很简单,但是假设我们的项目大了,代码量一多,这件事就会变得困难,事实上我们编程需要遵循一个叫做开闭原则的东西,就是对于代码的修改关闭,对于代码的扩充开启,我们可以看到我并没有去修改show_name和show_age, 而是新加了一个test函数,这个函数的作用就是对show_name和show_age功能的扩充,这样做的话代码的可维护性是可以大大提高的。第二种做法就是我们在主函数里面添加输出语句,但是这样做的话代码又过于死板。从上述例子中,我们可以从中提取出一个设计模式-------装饰器模式.

装饰器模式

装饰器模式是23种设计模式中的一种,我们通过一个例子来讲解什么是装饰器模式,比如说我们又如下代码:

#include <iostream>
using namespace std;void test() {for (int i = 0; i < 100000; i++) {// do something...}
}int main() {test();return 0;
}

现在我们有一个函数test,然后我现在又如下需求,就是计算test函数运行的时间,下列是一般的做法:

#include <iostream>
#include <ctime>
using namespace std;void test() {for (int i = 0; i < 100000; i++) {// do something...}
}int main() {clock_t start = clock();test();clock_t end = clock();cout << "time:" << end - start << "ms" << endl;return 0;
}

我们这样做确实可以知道解决这个需求,但是万一我程序中很多的地方都需要计算时间呢,难道我每个的地方都加上这样的代码吗?很明显这样的代码过于冗余,这时候我们就可以使用我们的装饰器模式来解决这个需。

#include <iostream>
#include <ctime>
using namespace std;void test() {for (int i = 0; i < 100000; i++) {// do something...}
}void warp_test(void (*test_ptr)()) {clock_t start = clock();test_ptr();clock_t end = clock();cout << "time:" << end - start << "ms" << enddl;
}int main() {//test();wrap_test(test);return 0;
}

我们可以看到我又增加了一个叫做wrap_test的函数,这个函数的参数就是一个函数指针,在函数内部,我们可以通过函数指针来调用外部的函数,并且多了计算时间的功能,并且我没有对test函数做出任何的修改,这样做既符合我们的开闭原则,而且代码的复用性也可以提高,比如说我们日后又有类似于test这样的函数(返回值为void,参数列表为空),那么我们仍然可以使用wrap_test这个函数去计算运行的时间,我们称wrap_test这样的函数为test的装饰器,这就是装饰器模式的核心内容。

函数指针的用法以及用途详解相关推荐

  1. Sklearn中predict_proba函数用法及原理详解

    Sklearn中predict_proba函数用法及原理详解(以logistic回归为例) 网上对predict_proba的数学原理解释的太少了,也不明确,特意总结一下,并给出有些不能用该方法的原因 ...

  2. python cut函数_基于python cut和qcut的用法及区别详解

    我就废话不多说了,直接上代码吧: from pandas import Series,DataFrame import pandas as pd import numpy as np from num ...

  3. python Format()函数的用法___实例详解(一)(全,例多)___各种格式化替换,format对齐打印

    python Format()函数的用法___实例详解(一)(全,例多) (格式化替换,关键字替换,列表字典替换,类格式化, 魔法函数格式化,对齐及填充格式化,format对齐打印) 本篇目录内容:

  4. Python中Print()函数的用法___实例详解(二)(全,例多)

    Python中Print()函数的用法___实例详解(二)(全,例多) 目录 十一.Print()小例子 十二.Print()中文输入显示乱码问题 十三.Print()写入文件 十四.print()在 ...

  5. 定时器 槽函数没执行_Web服务器项目详解 07 定时器处理非活动连接(上)

    点击"两猿社" 关注我们 Web服务器详解目录 00 项目概述 01 线程同步机制包装类 02 半同步/半反应堆线程池(上) 03 半同步/半反应堆线程池(下) 04 http连接 ...

  6. 【转】typedef函数指针的用法(C++)

    原文: typedef函数指针的用法(C++) 代码简化, 促进跨平台开发的目的. typedef 行为有点像 #define 宏,用其实际类型替代同义字. 不同点:typedef 在编译时被解释,因 ...

  7. php 查找键名,array_key_exists()函数搜索数组键名步骤详解

    这次给大家带来array_key_exists()函数搜索数组键名步骤详解,array_key_exists()函数搜索数组键名的注意事项有哪些,下面就是实战案例,一起来看一下. array_key_ ...

  8. groupby的用法及原理详解

    groupby的用法及原理详解_回家养老-CSDN博客_groupby 写在前面的话:用了好久group by,今天早上一觉醒来,突然感觉group by好陌生,总有个筋别不过来,为什么不能够sele ...

  9. 哈希算法的原理和用途详解

    什么是哈希算法?哈希是一种加密算法,也称为散列函数或杂凑函数.哈希函数是一个公开函数,可以将任意长度的消息M映射成为一个长度较短且长度固定的值H(M),称H(M)为哈希值.散列值(Hash Value ...

最新文章

  1. VS2010没有Intellisense(智能感知)的解决办法
  2. 百度前端学院---斌斌学院---任务demo---1
  3. 映射文件xxx.hbm.xml下的各元素结构
  4. 2016.9.24 の 測試
  5. PHP-Redis扩展安装 error: ext/standard/php_smart_str.h: No such file or directory
  6. 7-113 堆栈操作合法性 (20 分)
  7. Python面向对象编程案例:封装数据库增删改查操作
  8. java day32【HTML标签:表单标签 、CSS】
  9. 不平衡数据集_我们的不平衡数据集
  10. 如何从python官网下载模块-Python各种模块下载及安装配置
  11. visio输出论文用矢量图
  12. ubuntu_linux命令TX2学习总结
  13. 2021年美国大学生数学建模竞赛C题思路分析
  14. 2022美国科学院院士名单公布:图灵奖得主、龙书作者Alfred V. Aho当选!
  15. ecshop dwt替换为html,修改ecshop模板中lbi和dwt文件需知
  16. 四川省乐山市谷歌高清卫星地图下载
  17. 音视频技术开发周刊 | 274
  18. 前端定期小复盘, 每期都有小收获(一)
  19. 树莓派开发——基础配置
  20. 在共享主机上使用Screen for Human Beings:Byobu

热门文章

  1. MATLAB的图像显示方法
  2. python正则表达式介绍
  3. AutoCAD中凸度的概念以及求圆弧的凸度
  4. SQL Server部分知识的整理
  5. 更改Colab的CUDA以及cudnn
  6. Mybatis教程之Mybatis配置篇
  7. 计算机怎么登录用户名和密码忘了怎么办,忘记了路由器的登录用户名与密码怎么办...
  8. Error while Launching activity
  9. vue3跨组件传值(爷孙组件传值)
  10. 矩阵分析: Hilbert行列式