利用回调函数消灭大量分支语句if,case

2023-10-30

1、背景

有这样一个场景,常见的通讯程序中,根据不同的消息类型,调用不同的处理函数。

类似于处理登陆、退出登陆、发送消息等类型。

上古操作可能会是这样的代码:

void dealLogin()
{
    // ...
    std::cout << "received login info, do somthing" << std::endl;
}

void dealLogout()
{
    // ...
    std::cout << "received logout info, do somthing" << std::endl;
}

void recv(char* data)
{
    Package* pack = (Package*)data;
    if (pack->msg.type == 1) // login
    {
        dealLogin();
    }
    else if (pack->msg.type == 2) // logout
    {
        dealLogout();
    }
//    else if (...) // 大量if语句
//    {
//       ...
//    }
}

缺点:如果type很多,那么recv()中if就会越多,且后续若想增加一个type,则在recv()中代码一定是惨不忍睹。

各种type的处理糅合到一起,耦合性很强。那么怎么来解耦,想象一下,其实消息处理函数只是与type相关,则只要能根据type找到

处理函数即可,回调可以很好地解决这个问题。

2、解决办法

在一个数组中保存type与处理函数指针,在收到消息时,从数组中找出type对应的处理函数,进行调用。

再后续增加type时,只需要写一个处理函数,并添加到数组中即可,recv()中代码不用改。

int dealLogin(const char *para)
{
    // ...
    std::cout << "received login info:" << para << std::endl;
    return 0;
}

int dealLogout(const char *para)
{
    // ...
    std::cout << "received logout info:" << para << std::endl;
    return 0;
}

struct MsgCmd
{
    int type;                          // msg type
    int (*func)(const char *para);     // 函数指针
};

// 类型与处理函数绑定
struct MsgCmd commands[] =
{
    {1, dealLogin},
    {2, dealLogout},
};

void recv(char* data)
{
    Package* pack = (Package*)data;
    for (int i = 0; i < sizeof (commands) / sizeof (MsgCmd); i++)
    {
        if (pack->msg.type == commands[i].type)
        {
            (*commands[i].func)("123");
            break;
        }
    }
}

结果:

若想做的更加方便,可以用宏将函数定义与添加数组这2个操作合并,在用宏定义处理函数时,自动完成添加至数组。

好了,目标已经达成,消灭了大量if else/switch case。

 


若对你有帮助,欢迎点赞、收藏、评论,你的支持就是我的最大动力!!!

同时,阿超为大家准备了丰富的学习资料,欢迎关注公众号“超哥学编程”,即可领取。

本文涉及工程代码,公众号回复:05CallbackFunction,即可下载

在这里插入图片描述

 

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

利用回调函数消灭大量分支语句if,case 的相关文章

  • 向进度条添加百分比文本 C#

    我有一个方法可以显示进程栏何时正在执行以及何时成功完成 我工作得很好 但我想添加一个百分比 如果完成 则显示 100 如果卡在某个地方 则显示更少 我在网上做了一些研究 但我无法适应我正在寻找的解决方案 这是我的代码 private voi
  • 用于代数简化和求解的 C# 库 [关闭]

    Closed 这个问题正在寻求书籍 工具 软件库等的推荐 不满足堆栈溢出指南 help closed questions 目前不接受答案 网络上有很多代数求解器和简化器 例如 algebra com 上不错的代数求解器和简化器 然而 我正在
  • Directory.Delete 之后 Directory.Exists 有时返回 true ?

    我有非常奇怪的行为 我有 Directory Delete tempFolder true if Directory Exists tempFolder 有时 Directory Exists 返回 true 为什么 可能是资源管理器打开了
  • 提交后禁用按钮

    当用户提交付款表单并且发布表单的代码导致 Firefox 中出现重复发布时 我试图禁用按钮 去掉代码就不会出现这个问题 在firefox以外的任何浏览器中也不会出现这个问题 知道如何防止双重帖子吗 System Text StringBui
  • 在 DataView 的 RowFilter 中选择 DISTINCT

    我试图根据与另一个表的关系缩小 DataView 中的行范围 我使用的 RowFilter 如下 dv new DataView myDS myTable id IN SELECT DISTINCT parentID FROM myOthe
  • 错误:表达式不产生值

    我尝试将以下 C 代码转换为 VB NET 但在编译代码时出现 表达式不产生值 错误 C Code return Fluently Configure Mappings m gt m FluentMappings AddFromAssemb
  • 使用接口有什么好处?

    使用接口有什么用 我听说它用来代替多重继承 并且还可以用它来完成数据隐藏 还有其他优点吗 哪些地方使用了接口 程序员如何识别需要该接口 有什么区别explicit interface implementation and implicit
  • 将 Word 文档另存为图像

    我正在使用下面的代码将 Word 文档转换为图像文件 但是图片显得太大 内容不适合 有没有办法渲染图片或将图片保存到合适的尺寸 private void btnConvert Click object sender EventArgs e
  • 在 Visual Studio 2010 中从 Fortran 调用 C++ 函数

    我想从 Fortran 调用 C 函数 为此 我在 Visual Studio 2010 中创建了一个 FORTRAN 项目 之后 我将一个 Cpp 项目添加到该 FORTRAN 项目中 当我要构建程序时出现以下错误 Error 1 unr
  • 是否有实用的理由使用“if (0 == p)”而不是“if (!p)”?

    我倾向于使用逻辑非运算符来编写 if 语句 if p some code 我周围的一些人倾向于使用显式比较 因此代码如下所示 if FOO p some code 其中 FOO 是其中之一false FALSE 0 0 0 NULL etc
  • 如何在 32 位或 64 位配置中以编程方式运行任何 CPU .NET 可执行文件?

    我有一个可在 32 位和 64 位处理器上运行的 C 应用程序 我试图枚举给定系统上所有进程的模块 当尝试从 64 位应用程序枚举 32 位进程模块时 这会出现问题 Windows 或 NET 禁止它 我认为如果我可以从应用程序内部重新启动
  • CMake 无法确定目标的链接器语言

    首先 我查看了this https stackoverflow com questions 11801186 cmake unable to determine linker language with c发帖并找不到解决我的问题的方法 我
  • 使用管道时,如果子进程数量大于处理器数量,进程是否会被阻塞?

    当子进程数量很大时 我的程序停止运行 我不知道问题是什么 但我猜子进程在运行时以某种方式被阻止 下面是该程序的主要工作流程 void function int process num int i initial variables for
  • Cmake 链接共享库:包含库中的头文件时“没有这样的文件或目录”

    我正在学习使用 CMake 构建库 构建库的代码结构如下 include Test hpp ITest hpp interface src Test cpp ITest cpp 在 CMakeLists txt 中 我用来构建库的句子是 f
  • 将 MQTTNet 服务器与 MQTT.js 客户端结合使用

    我已经启动了一个 MQTT 服务器 就像this https github com chkr1011 MQTTnet tree master例子 该代码托管在 ASP Net Core 2 0 应用程序中 但我尝试过控制台应用程序 但没有成
  • 不同类型指针之间的减法[重复]

    这个问题在这里已经有答案了 我试图找到两个变量之间的内存距离 具体来说 我需要找到 char 数组和 int 之间的距离 char data 5 int a 0 printf p n p n data 5 a long int distan
  • 如果没有抽象成员,基类是否应该标记为抽象?

    如果一个类没有抽象成员 可以将其标记为抽象吗 即使没有实际理由直接实例化它 除了单元测试 是的 将不应该实例化的基类显式标记为抽象是合理且有益的 即使在没有抽象方法的情况下也是如此 它强制执行通用准则来使非叶类抽象 它阻止其他程序员创建该类
  • 方法优化 - C#

    我开发了一种方法 允许我通过参数传入表 字符串 列数组 字符串 和值数组 对象 然后使用这些参数创建参数化查询 虽然它工作得很好 但代码的长度以及多个 for 循环散发出一种代码味道 特别是我觉得我用来在列和值之间插入逗号的方法可以用不同的
  • C++ 条件编译

    我有以下代码片段 ifdef DO LOG define log p record p else define log p endif void record char data 现在如果我打电话log hello world 在我的代码中
  • 如何从 ODBC 连接获取可用表的列表?

    在 Excel 中 我可以转到 数据 gt 导入外部数据 gt 导入数据 然后选择要使用的数据源 然后在提供登录信息后 它会给我一个表格列表 我想知道如何使用 C 以编程方式获取该列表 您正在查询什么类型的数据源 SQL 服务器 使用权 看

随机推荐

  • Pywinauto+某应用程序(学习至第9讲)--受阻

    文章目录 Pywinauto 某应用程序 学习至第9讲 受阻 问题点 1 安装第三方库 2 自动化的切入点 后端技术 程序个数 3 程序辅助检查工具的使用 inspect spy 4 pywinauto打开指定的应用程序 XXX exe 5
  • Matroska文件的SRT Subtitle

    1 SRT简单介绍 SRT是一种比较流行的文本字幕 因为是文本格式 所以就比较小了 因为其制作规范简单 一句时间代码 一句字幕 使得制作修改就相当简单 配合上 style文件还能让srt自带一些字体上的特效等 SRT文件中的字幕包括四个部分
  • 批量调整word 图片大小

    打开文档后 按Alt F11 在左边Porject下找到ThisDocument 右键插入模块 贴上下面的Sub Macro For Each iShape In ActiveDocument InlineShapesiShape Heig
  • 【安卓学习之常见问题】文件分享--文件不存在

    安卓学习之常见问题 文件分享 文件不存在 系列文章目录 提示 这里是收集了和文件分享有关的文章 安卓学习之常见问题 android路径及文件问题 安卓学习之常见问题 文件分享 文件不存在 文章目录 安卓学习之常见问题 文件分享 文件不存在
  • LabVIEW深度相机与三维定位实战(下)

    博客主页 virobotics的CSDN博客 LabVIEW深度学习 人工智能博主 所属专栏 LabVIEW深度学习实战 上期文章 LabVIEW深度相机与三维定位实战 上 如觉得博主文章写的不错或对你有所帮助的话 还望大家多多支持呀 欢迎
  • 什么是内存泄漏,一看就懂,一学就会!!大白话解释内存泄漏!通俗易懂!

    在 32 位环境下 一个程序占用 4GB 的内存 其中 内核空间 是被操作系统占用的 我们没法直接干预 保留区域 也不用来存储数据 只用作一些特殊目的 比如 你可以让空指针指向这里 除了这两个区域 剩下的那些内存才是被我们自己编写的程序所占
  • Oracle数据库常见版本

    在Oracle数据库的发展中 数据库一直处于不断升级状态 有以下几个版本 Oracle 8 Oracle 8i Oracle 8i表示Oracle正式向Internet上开始发展 其中i表示就是internet Oracle 9i Orac
  • 带你玩转Spring Cloud Tencent(一)概述

    项目地址 spring cloud tencent 介绍 Spring Cloud Tencent 是腾讯开源的一站式微服务解决方案 Spring Cloud Tencent 实现了Spring Cloud 标准微服务 SPI 开发者可以基
  • PHP密码复杂性验证,JS检查密码强度 检查密码复杂度

    pass keyup function e var strongRegex new RegExp 8 A Z a z 0 9 W g var mediumRegex new RegExp 7 A Z a z A Z 0 9 a z 0 9
  • 电信光猫天翼网关usb插U盘共享文件

    ftp用不了 samba可以用 1 在电脑文件管理器中输入 192 168 1 1打开 在弹出框中输入光猫背后的账号密码登录即可打开共享的U盘 2 在手机ES文件管理器中 点右上角三点 新建 在弹出框中填入192 168 1 1和选择sam
  • Nginx 官网及中文官网

    英语官方 http nginx org 中文文档 http www nginx cn doc 转载于 https blog 51cto com hacker3389 1877270
  • 什么是大数据(转自知乎)

    声明 纯属个人收藏用 什么是大数据 大数据只是一个空洞的商业术语 就跟所谓的商业智能一样空洞无物 当然 这并不是说大数据没有意义 只是对于不同的人有不同的含义 A 对于投资人和创业者而言 大数据是个热门的融资标签 就和前几年流行的 SoLo
  • 磁盘快照技术

    一 概念解释 像照相机一样 机器快门一闪 很快就把刚刚的人像停留在了相纸上 存储系统中的数据 快照 与我们生活中所说的 照片 非常相似 所不同的是 照片的对象不是人 而是数据 如同照片留住了我们过去的摸样和岁月 快照把数据在某一时刻的映像也
  • 【数据结构】——顺序表介绍(独家介绍,小白必看!!)

    重点和易错点都用彩笔标记出来了 放心食用 数据结构分为线性表和非线性表 今天我们要学习的顺序表就是线性表中的一个小类 那么 何为线性表 线性表是指n个具有相同性质的数据元素的有限序列 常见的线性表有 顺序表 链表 栈 队列 字符串等等 注意
  • java非递归遍历二叉树 - Kaiqisan

    大家好 都吃晚饭了吗 我是Kaiqisan 是一个已经走出社恐的一般生徒 都说所有的递归都可以使用非递归的方式来解决 所以这次来一起康康非递归版本的二叉树的遍历 递归的本质就是不断往栈中塞入待执行代码 然后在代码块被执行的时候就会被调用执行
  • java时间格式化错误_java – SimpleDateFormat显示错误的分钟,秒和毫秒

    我已经编写了这个示例程序 我希望将日期转换为另一种格式 使用简单的日期格式时 我看不到预期的日期 public class TestDate param args public static void main String args Si
  • 聊一聊如何用IDEA追踪Bug?

    Debug用来追踪代码的运行流程 通常在程序运行过程中出现异常 启用Debug模式可以分析定位异常发生的位置 以及在运行过程中参数的变化 通常我们也可以启用Debug模式来跟踪代码的运行流程去学习三方框架的源码 Debug开篇 首先看下ID
  • 仅仅上线一小时,下载量就破10W!阿里内部Java性能优化实战手册

    祸兮福之所倚福兮祸之所伏 上学的时候对这句话不以为然 但是在社会上走的时间越长越觉得有道理 前不久好兄弟和领导闹矛盾裸辞了 身为好兄弟的我总不能干看着吧 总要帮他找工作的 你们应该不会想我和他一起裸辞吧 大学的师兄有好几个在大厂 平常关系还
  • 在 Dockerfile 中 CMD 和ENTRYPOINT可以混着用吗?

    在 Dockerfile 中 CMD 和ENTRYPOINT可以混着用吗 在 Dockerfile 中 CMD 和 ENTRYPOINT 是两个不同的指令 它们可以单独使用 也可以结合使用 CMD 指令用于指定容器启动时默认执行的命令 它可
  • 利用回调函数消灭大量分支语句if,case

    1 背景 有这样一个场景 常见的通讯程序中 根据不同的消息类型 调用不同的处理函数 类似于处理登陆 退出登陆 发送消息等类型 上古操作可能会是这样的代码 void dealLogin std cout lt lt received logi