原作者:庄严
链接:https://www.zhihu.com/question/349498301/answer/1492253088

超级精彩的解说(用c++讲的),但还是忍不住想摘录下来

函数指针让“函数”可以是一个“变量”。

通常我们认为一件事由“动作”和“数据”组成,比如“小明泡妞”中,小明是数据,妞是数据,泡是动作。动作和数据边界清晰,各自含义也直观。

把动作数据化,真的是各种编程语言一路下来心心念念的要有各种实现各种改进的念想。就以典型面向过程式的C语言为例:函数指针在C语言中就存在;并且相比支持面向对象的其它语言,比如C++,函数指针在C语言更加广泛地应用;地位很高。用不好甚至不会用函数指针的C程序员,基本就是“雏”。

事实上在现实的生活经验中,人类就有大量需要"动作数据化"的时候。

以“小明泡妞“为例。小明不是雏,相反,他是渣:他同时向5个女生发起约会;这个5个女生和他的亲近关系各不相同,以小红和小白为例:小红每次吃饭都拿脚在餐桌下勾小明……的拖鞋;小白则只要小明盯她超过0.05秒就会甩一巴掌大骂“流氓”。

所以,同样是“泡”由于泡的对象不同,导致五种“泡”的动作包含的实质内容,天差地别,近乎没有共性——尽管从外部看,它们都是一类操作:泡妞。

五个几乎没有共性的操作,你是要分5个函数各自写,还是揉入1个函数用一堆if/else来判断,区分?显然是个正常人都会选前者。于是就有:

void PaoA(char const* name) { cout << "和" << name << "看文艺电影" << endl; }
void PaoB(char const* name) { cout << "陪" << name << "逛街购物,我花钱" << endl;  }
void PaoC(char const* name) { cout << name << ",走,到军博看大炮去!" << endl; }
void PaoD(char const* name) { cout << name << ",我们来谈人生好吗?" << endl; }
void PaoE(char const* name) { cout << name << ",川菜还是撸串?" << endl; }

你以为我们是讲多态——小明依据最终成功约到的女生不同,执行不同的Pao行为?

不是不是,我想说的是小明把5位女生都成功约好,并且时间安排得非常合理,于是这个周末,小明要做的,就是逐个调用一遍就对了! 

逐个调用?现在有两种做法,一个是:

void WeekendDate()
{PaoA("丁晓晓");PaoB("贾玲玲");PaoC("林子绿");PaoD("王梦");PaoE("水原希");
}

这就是你说的“直接调用函数”——但别忘了我们的关键业务需求:小明之渣。小明下周可能又约另外5个甚至6个;并且约的对象也在变;约到后泡的操作也会变……如此,就会想到最好还是做两个数组,一个存储女生(的姓名),一个存储对应的“泡”法:

#include <iostream>using namespace std;void PaoA(char const* name) { cout << "和" << name << "看文艺电影" << endl; }
void PaoB(char const* name) { cout << "陪" << name << "逛街购物,我花钱" << endl;  }
void PaoC(char const* name) { cout << name << ",走,到军博看大炮去!" << endl; }
void PaoD(char const* name) { cout << name << ",我们来谈人生好吗?" << endl; }
void PaoE(char const* name) { cout << name << ",川菜还是撸串?" << endl; }char const* names [] =
{"丁晓晓", "贾玲玲", "林子绿", "王梦", "水原希"
};using F = void (*)(char const*);F functions [] =
{PaoA, PaoB, PaoC, PaoD, PaoE
};void WeekendDate()
{for (auto i=0; i<sizeof(names)/sizeof(names[0]); ++i){functions[i](names[i]);}
}int main()
{WeekendDate();
}

以上代码100%可直接跑,输出如下:

和丁晓晓看文艺电影
陪贾玲玲逛街购物,我花钱
林子绿,走,到军博看大炮去!
王梦,我们来谈人生好吗?
水原希,川菜还是撸串?   

下周,再下周……当约的人有变化,我们就修改names数组,再对应修改每个人名对应可执行的动作(也已经是数组)就好了;至于那个 “WeekendDate”所要表达的周末约会都不用变啦……

事主小明表示担心:再下下周,同样是5个人,同样是周末,同样是约会,但我想开群体活动,五个一起来!

多大的事,别说理论上的"同时",就是真要多线程并发(小明有四个分身)约会也没问题啊!事实上业务需求越复杂,往往代表数据之间的关系或耦合也越紧密,此时names和 functions这样的数据化设计,也就越能体现它们的正确性。以并发为例:

#include <iostream>
#include <thread>using namespace std;void PaoA(char const* name) { cout << "和" << name << "看文艺电影" << endl; }
void PaoB(char const* name) { cout << "陪" << name << "逛街购物,我花钱" << endl;  }
void PaoC(char const* name) { cout << name << ",走,到军博看大炮去!" << endl; }
void PaoD(char const* name) { cout << name << ",我们来谈人生好吗?" << endl; }
void PaoE(char const* name) { cout << name << ",川菜还是撸串?" << endl; }using F = void (*)(char const*);F functions [] =
{PaoA, PaoB, PaoC, PaoD, PaoE
};char const* names [] =
{"丁晓晓", "贾玲玲", "林子绿", "王梦", "水原希"
};constexpr std::size_t N = sizeof(names)/sizeof(names[0]);void WeekendDate()
{for (auto i=0; i<N; ++i){functions[i](names[i]);}
}void CrazyWeekendDate()
{    std::thread trds [N];for (std::size_t i=0; i<N; ++i){auto trd = std::thread([i]{functions[i](names[i]);});trds[i] = std::move(trd);}for (auto& trd: trds){trd.join();}
}int main()
{std::cout << "辛苦的周末:\n";WeekendDate();std::cout << "\n";std::cout << "混乱的周末:\n";CrazyWeekendDate();
}

运行后的一种输出可能是:

辛苦的周末:
和丁晓晓看文艺电影
陪贾玲玲逛街购物,我花钱
林子绿,走,到军博看大炮去!
王梦,我们来谈人生好吗?
水原希,川菜还是撸串? 混乱的周末:
水原希,川菜还是撸串?
王梦,我们来谈人生好吗?
林子绿,走,到军博看大炮去!
陪贾玲玲逛街购物,我花钱
和丁晓晓看文艺电影

如果改回“直 接 调 用”,那个线程创建的过程,大概是这样的:

        auto trd = std::thread([i]{if (names[i] == std::string("丁晓晓")){paoA(names[i);}else if (names[i] == std::string("贾玲玲")){paoB(names[i);  }else if (names[i] == std::string("林子绿")){paoC(names[i);  }else if (...){....}});

或者改用判断 “i”的值——但那样会更不直观……

悟到重点了没?

1. 解决问题,若不能依赖于(优雅的)数据结构, 那就得依赖于(粗鄙的)流程结构。

2. 函数通常使用流程结构来组织,一旦函数可以数据化,就能让数据结构也过来帮忙表达复杂的操作。

为什么不直接调用函数而要使用函数指针?相关推荐

  1. Go 学习笔记(16)— 函数(02)[函数签名、有名函数、匿名函数、调用匿名函数、匿名函数赋值给变量、匿名函数做回调函数]

    1. 函数签名 函数类型也叫做函数签名,可以使用 fmt.Printf("%T") 格式化参数打印函数类型. package mainimport "fmt"f ...

  2. Go 学习笔记(15)— 函数(01)[函数定义、函数特点、多值返回、实参形参、变长参数,函数作为参数调用]

    1. 函数定义 Go 语言最少有个 main() 函数.函数声明告诉了编译器函数的名称,返回类型和参数. func funcName(parameter_list)(result_list) {fun ...

  3. jq 直接调用php文件_PHP的函数类别和变量命名

    内部函数: PHP可以在函数内部再声明函数 * 目的就是在函数的内部调用 * 就是用来帮助外部函数完成一些子功能的. * 递归函数: 就是在自己内部调用自己的函数名 * 重用函数(使用自己定义的函数库 ...

  4. java用if语句调用方法_J2SE中main函数中的if语句想要调用另一个类的方法怎么能实现?...

    日常生活中,要完成一件复杂的功能,我们总是习惯把"大功能"分解为多个"小功能"以实现.在C++程序的世界里,"功能"可称呼为"函数 ...

  5. 解决onclick事件调用其它带参数的函数方法无效的问题

    不解以下现象: 原始版本代码:可跳过代码直接看解决方案部分.前面的文字是在解决问题时的思考. <body> <div id="div1"><form ...

  6. python调用js库中的函数_Python 调用JS文件中的函数

    Python 调用JS文件中的函数 1.安装PyExecJS第三方库 2.导入库:import execjs 3.调用JS文件中的方法 Passwd = execjs.compile(open(r&q ...

  7. python函数+定义+调用+多返回值+匿名函数+lambda+高级函数(reduce、map、filter)

    python函数+定义+调用+多返回值+匿名函数+lambda+高级函数(reduce.map.filter) Python 中函数的应用非常广泛,比如 input() .print().range( ...

  8. python如何调用cpp文件的接口函数_C++中嵌入Python调用

    python嵌入到C++中 把python嵌入的C++里面需要做一些步骤 安装python程序,这样才能使用python的头文件和库 在我们写的源文件中增加"Python.h"头文 ...

  9. 主页面调用iframe里面匿名Javascript函数的问题

    2019独角兽企业重金招聘Python工程师标准>>> 这个问题出现已经几天了,今天终于解决了,特此记录下来. 问题的出现始于我在HTML5下采用Reveal.js来播放PPT和使用 ...

  10. python调用php命令行,python调用php函数 python怎样调用php文件中的函数详解

    前言 python调用php代码实现思路:php文件可通过在terminal中使用php命令行进行调用,因此可使用python开启子进程执行命令行代码.函数所需的参数可通过命令行传递. 测试环境 1. ...

最新文章

  1. 第三次作业(词频统计及其效能分析)
  2. javascript tabIndex属性
  3. mp4文件格式解析(一)
  4. 热式气体质量流量计检定规程_宁夏热式气体质量流量计价位,玻璃管液位计怎么样...
  5. 2019国内某知名科技公司技术资料
  6. python 脚本所在目录,Python 获取当前所在目录的方法详解
  7. 图像处理里面的的尺度什么?
  8. 大话数据结构学习笔记
  9. 校园邮箱免费激活idea教程
  10. 诺基亚Nokia的PC套件导出短信乱码问题解决(转)
  11. 单片机控制步进电机程序c语言正反转停止,51单片机步进电机正反转停止实验-C51源代码...
  12. 青龙面板之B站任务天选时刻
  13. 电子学会图形化二级编程题解析含答案:魔法星空
  14. 自己动手,编写神经网络程序,解决Mnist问题,并网络化部署-6CBIR模拟问题
  15. 远程办公常态化,网络安全迫在眉睫,“零信任安全”的风口到了吗?
  16. python爬虫 模拟登录人人网过程解析
  17. 鹅厂4045天,老总监我毕业了
  18. 【VBA研究】用VBA取得EXCEL任意列有效行数
  19. 人类附身机器人获得永生_2045年人类将永生不死,用纳米机器人改造人体以获长生...
  20. 二进制兼容ABI-C++库注意事项

热门文章

  1. 忘记Excel文件保护密码,怎么解?
  2. ArrayBlockingQueue
  3. 怎么通过苹果HEIC图片转换器将heic格式转换为其他格式?
  4. viata home basic iis 无法运行 asp 程序
  5. 一文纵览EMAS 到底内含多少阿里核心技术能力
  6. cmsplus实战之仿[我扫网]之一:仿站开发工程简要说明及目的
  7. lnmp搭建qq农场(小白教程)
  8. 2021道路运输从业安全员考试模拟题库多选题及答案解析
  9. 会助力客户案例:“四川省卷烟营销网络建设工作推进会”
  10. scp传输文件 Permission denied, please try again