如何用c++表驱动替换if/else和switch/case语句

目录
  • C++的表驱动法
    • 一、常用示例
    • 二、表驱动法
    • 三、C++实现注意
    • 四、实用案例

C++的表驱动法

目的:使用表驱动法,替换复杂的if/else和switch/case语句。

一、常用示例

以switch为例,常用示例如下:

Funcition()
{
    switch (key)
    {
        case key1:
            statements 1;
            break;
        case key2:
            statements 2;
            break;
        ...
        case keyn:
            statements n;
            break;
        default:
            break;
    }
}

上述switch代码段,实际集成了3种类型逻辑:

1.    实现关键字的处理代码;
2.    将关键字与处理代码关联;
3.    以关键字选择分支,执行处理代码;

我们将在一个switch代码中维护3种变化。将1的处理代码段,模块化为函数后,变化点减少为2个。

在分支增加到几十个时,代码维护性变得很差;而且switch对非整数类型无能为力。

二、表驱动法

做法:

1.  将变化点2,做成一个[关键字:处理函数]映射结构(推荐map容器),在独立函数中赋值。
2.  将变化点3,做成一个查找关键字,执行对应函数的简单函数

好处:

1.  独立出“选择分支”变化点,变为固定的处理流程。
2.  独立出“关键字和处理函数的关联”,易于维护。

限制条件:

1.  处理函数类型一样(这在C++中不成问题);
2.  处理函数简单,但每个函数有差异(如果处理较为复杂,请使用创建型设计模式)

扩展:

1.  对于处理函数由符合条件分支情况,变化点2使用list结构,按优先级关联处理函数,使用 “职责链”形式的表驱动法。

三、C++实现注意

代码:

// 3个文件,Client.cpp, TableDrave.h, TableDrive.cpp

// vvvvv Client.cpp begin

// ------------------------------------------------------------
// Name         :   Client.cpp
// Description  :   调用接口
// History      :
// ------------------------------------------------------------

#include    "TableDrive.h"

// ------------------------------------------------------------

int main()
{
    TableDrive test;

    test.HandleKeyword(KEYWORD_A);
    test.HandleKeyword(KEYWORD_B);
    test.HandleKeyword(KEYWORD_C);
    test.HandleKeyword(KEYWORD_START);
    test.HandleKeyword(KEYWORD_D);

    return 0;
}

// ^^^^^ Client.cpp end

// vvvvv TableDrive.h begin

// ------------------------------------------------------------
// Name         :   TableDrive.h
// Description  :   表驱动头文件
// History      :
// ------------------------------------------------------------

#ifndef     _TEST_DRIVE_H
#define     _TEST_DRIVE_H

#include    <map>

// ------------------------------------------------------------

// 测试用关键字
enum KEYWORD
{
    KEYWORD_START = -1,

    KEYWORD_A = 0,
    KEYWORD_B,
    KEYWORD_C,
    KEYWORD_D,  

    KEYWORD_END,
};

// ------------------------------------------------------------

// 可以使用 std:: 单个引用
using namespace std;

class TableDrive
{
public: 

    // ------------------------------------------------------------
    // Description :
    //      根据关键字,执行处理函数
    // Parameters :
    //      string keyword,关键字
    // Return Value :
    //      bool,true,函数执行成功,false,找不到键字对应的函数,或函数执行失败
    // Errors :
    //      无
    // ------------------------------------------------------------
    bool HandleKeyword(int keyword);

    // ------------------------------------------------------------
    // Description :
    //      关联关键字到处理函数
    // Parameters :
    //      无
    // Return Value :
    //      bool,true,正常,false,异常
    // Errors :
    //      无
    // ------------------------------------------------------------
    bool MapKeyToHandle();  

    TableDrive();

    ~TableDrive();

private:

    // vv 处理函数,true,执行成功,false,执行失败
    bool HandleKeyA();
    bool HandleKeyB();
    bool HandleKeyC();
    // ^^

private:

    // :TRICKY: 成员函数指针定义
    typedef bool (TableDrive:: *PHandle)(void);
    map<int, PHandle>   m_KeyToHandle;      // 关键字对应处理函数
};

#endif

// ^^^^^ TableDrive.h end

// vvvvv TableDrive.cpp begin

// ------------------------------------------------------------
// Name         :   TableDrive.cpp
// Description  :   表驱动实现文件
// History      :
// ------------------------------------------------------------

#include    <stdio.h>

#include    "TableDrive.h"

// ------------------------------------------------------------

// 根据关键字,执行处理函数
bool TableDrive::HandleKeyword(int keyword)
{
    typedef map<int, PHandle>::const_iterator CI;
    CI iter = m_KeyToHandle.find(keyword);

    // 没有搜索到关键字
    if (m_KeyToHandle.end() == iter)
    {
        printf("\n  @@ search Keyword %d fail!\n", keyword);
        return false;
    }

    // :TRICKY: 注意成员函数指针的引用格式
    PHandle pFunction = iter->second;
    return (this->*pFunction)();
}

TableDrive::TableDrive()
{
    printf("\n  vv TableDrive::TableDrive()\n");
    MapKeyToHandle();
}

TableDrive::~TableDrive()
{
    printf("\n  ^^ TableDrive::~TableDrive()\n");
}

// ------------------------------------------------------------

// 关联关键字到处理函数
bool TableDrive::MapKeyToHandle()
{
    m_KeyToHandle[KEYWORD_A]    = &TableDrive::HandleKeyA;
    m_KeyToHandle[KEYWORD_B]    = &TableDrive::HandleKeyB;
    m_KeyToHandle[KEYWORD_C]    = &TableDrive::HandleKeyC;

    return true;
}

// 处理函数 A
bool TableDrive::HandleKeyA()
{
    printf("\n  ** A, HandleKeyA()\n\n");

    return true;
}

bool TableDrive::HandleKeyB()
{
    printf("\n  ** B, HandleKeyB()\n\n");

    return true;
}

bool TableDrive::HandleKeyC()
{
    printf("\n  ** C, HandleKeyC()\n\n");

    return true;
}

// ^^^^^ TableDrive.cpp end

关注点:

主要关注3个点,维护第2、3点

1.   HandleKeyword(),根据关键字,执行处理函数。固定后基本不改变;
2.   MapKeyToHandle(),关联关键字到处理函数;
3.   Handle(),各个处理函数

成员函数指针使用注意

1.  声明格式,与C相比,函数指针前要包含类域;
typedef bool (TableDrive:: *PHandle)();

2.  声明位置,包含在类中,否则不能识别类域标志;

3.  赋值语法格式,与C相比,函数指针前要包含类域;
PHandle pFunction = &TableDrive::HandleKeyA;

4.  调用语法格式,与C相比,需要加上this,并以强制解引用方式调用;
(this->*pFunction)();

四、实用案例

1.  菜单调节。一个模块,有几十个菜单参数可以调节,每个菜单调节的步进、范围不同,但都是“触发消息、调节数值”流程。

2.  按键响应。多个按键,属于“按键,执行对应处理函数”流程。

3.  鼠标操控。不同状态下移动鼠标,属于“状态判断、响应鼠标处理函数”流程。

表驱动法

到此这篇关于使用c++表驱动法,替换复杂的if/else和switch/case语句的文章就介绍到这了,更多相关c++表驱动法内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • C++实现神经网络框架SimpleNN的详细过程

    目录 Features Dependencies Platform To Do Usage SimpleNN is a simple neural network framework written in C++.It can help to learn how neural networks work. 源码地址:https://github.com/Kindn/SimpleNN Features Construct neural networks. Configure optimizer a

  • Python实现switch/case语句

    目录 使用if-elif-elif-else 实现switch/case 使用字典 实现switch/case 在类中可使用调度方法实现switch/case 总结 使用if-elif-elif-else 实现switch/case 可以使用if-elif-elif..else序列来代替switch/case语句,这是大家最容易想到的办法.但是随着分支的增多和修改的频繁,这种代替方式并不很好调试和维护. 使用字典 实现switch/case 可以使用字典实现switch/case这种方式易维护,

  • 一篇文章带你了解C++中的显示转换

    目录 总结 命名的强制类型转换: 形式: cast-name<type>(expression); type是强制转换的类型,expression是强制转换的值.如果type是引用类型,则结果是左值.case-name是C++四种转换类型static_cast.dynamic_cast.const_cast和reinterpret_cast的一种. static_cast 可以被用于强制隐形转换(例如,non-const对象转换为const对象,int转型为double,等等)作用于对象,它还

  • c语言switch反汇编的实现

    目录 一.switch语句 1.在正向编码时,switch语句可以看做是if语句的简写 2.break在switch语句的妙用 二.switch语句的反汇编 1.当switch存在3个分支时 2.当switch语句出现四个分支时,编译器会产生大表 3.当switch存在多个分支,常量连续性相对不高时 补充:当case后的常量差距较大时 在分支较多的时候,switch的效率比if高,在反汇编中我们即可看到效率高的原因 一.switch语句 1.在正向编码时,switch语句可以看做是if语句的简写

  • C++关于类结构体大小和构造顺序,析构顺序的测试详解

    目录 总结 #include <iostream> using namespace std; /** 1. c++的类中成员若不加修饰符的话,默认是private 2. 调用构造函数时,先递归调用最顶级的父类构造函数,再依次到子类的构造函数. 3. 调用析构函数时相反,先调用最底层的子类析构函数,再依次到父类的构造函数. 4. 空类的sizeof(A)大小为1,多个空类继承后的子类大小也是1 */ class A{ public: A() { cout<<"A const

  • C语言流程控制之switch语句详解

    switch语句结构 switch(表达式) { case 判断值1; 语句组1: break; case 判断值2: 语句组2: break; case 判断值3: 语句组3: break; -- case 判断值n: 语句组n: break; default: 语句组n+1: break; } 表达式是选择条件,可以是单个变量也可以是组合的表达式,其最终的结果必须是一整数值,{}内的所有内容是switch语句的主体,内含多个case分支,判断值必须是一常量,case分支根据判断值标识条件选择

  • 如何用c++表驱动替换if/else和switch/case语句

    目录 C++的表驱动法 一.常用示例 二.表驱动法 三.C++实现注意 四.实用案例 C++的表驱动法 目的:使用表驱动法,替换复杂的if/else和switch/case语句. 一.常用示例 以switch为例,常用示例如下: Funcition() { switch (key) { case key1: statements 1; break; case key2: statements 2; break; ... case keyn: statements n; break; defaul

  • 如何用分表存储来提高性能 推荐

    首先,童家旺介绍了他认为的什么是优化:第一.做任何事情最快的方法就是什么也不做. ▲支付宝资深数据库架构师童家旺 第二.不访问不必要的数据:使用B*Tree/hash等方法定位必要的数据.使用column Store或分表的方式将数据分开存储.使用Bloom filter算法排除空值查询. 第三.合理的利用硬件来提升访问效率:使用缓存消除对数据的重复访问.使用批量处理来减少磁盘的Seek操作.使用批量处理来减少网络的Round Trip.使用SSD来提升磁盘访问效率. 响应时间和吞吐量之间的关系

  • mybatis-plus拦截器、字段填充器、类型处理器、表名替换、SqlInjector(联合主键处理)

    目录 组件介绍 表名处理器 字段填充器 类型处理器 补充 最近有个练手的小例子,大概就是配置两个数据源,从一个数据源读取数据写到另一个数据源,虽然最后做了出来,但是不支持事务...就当是对mybatis-plus/mybatis组件使用方式的记录吧,本次例子使用的仍是mybatis-plus 回忆一下mybatis核心对象: Configuration 初始化基础配置,比如MyBatis的别名等,一些重要的类型对象,如,插件,映射器,ObjectFactory和typeHandler对象,MyB

  • 复制数据库表中两个字段数据的SQL语句

    复制数据库表中两个字段数据的SQL语句 今天为表新添加一个字段,但又想与表中的另一个字段值相同,由于数据过多想通过sql语句实现,经测试下面的这句话确实很好用. 复制代码 代码如下: update jb51_temp set B=A jb51_temp 代表表明 A是有数值的字段,B是新添加的字段 ,记住空值表放到前面,如果set a=b,那么你的数值都为空了,就麻烦了,建议操作前备份数据库. 如何将一个字段里的值复制添加到另一个字段中 比如,有二个字段A和B,A字段是值是"我",B字

  • thinkphp中多表查询中防止数据重复的sql语句(必看)

    下面先来看看例子: table id name 1 a 2 b 3 c 4 c 5 b 库结构大概这样,这只是一个简单的例子,实际情况会复杂得多. select *, count(distinct name) from table group by name 结果: id name count(distinct name) 1 a 1 2 b 1 3 c 1 最后一 项是多余的,不用管就行了 tp2.0手册   搜索连贯操作 可看到相关的资料 SELECT cat_id, COUNT(*) AS

  • 分析Mysql表读写、索引等操作的sql语句效率优化问题

    上次我们说到mysql的一些sql查询方面的优化,包括查看explain执行计划,分析索引等等.今天我们分享一些 分析mysql表读写.索引等等操作的sql语句. 闲话不多说,直接上代码: 反映表的读写压力 SELECT file_name AS file, count_read, sum_number_of_bytes_read AS total_read, count_write, sum_number_of_bytes_write AS total_written, (sum_number

  • 如何用拦截表单的方法上传图片?

    <script language="vbs">sub send_&#111nclick                dim f       set f=document.ff      photo=trim(f.photo.value)              if photo="" then                    msgbox "嘻嘻,还没照片呢!",64 ,"上传"         

  • Oracle 查看表空间的大小及使用情况sql语句

    SQL1: 复制代码 代码如下: --1.查看表空间的名称及大小 SELECT t.tablespace_name, round(SUM(bytes / (1024 * 1024)), 0) ts_size FROM dba_tablespaces t, dba_data_files d WHERE t.tablespace_name = d.tablespace_name GROUP BY t.tablespace_name; --2.查看表空间物理文件的名称及大小 SELECT tables

  • sqlserver中向表中插入多行数据的insert语句

    下面把在sql吧里一位高手的解决方法,公布下.供大家参考: 假设有个表有 学号.姓名.学校 这三列 然后向这个表中插入 040501 孙明 山东大学 040502 李浩 山东师范 040503 王刚 烟台大学 怎么插入这三行数据啊~~~~~~~ 复制代码 代码如下: insert 表名 select '040504','孙明','山东大学' union select '040502','李浩','山东师范' union select '040503','王刚','烟台大学'

  • MySQL数据库表分区注意事项大全【推荐】

    表分区与数据库分区是不一样的那么碰到表分区使用时我们要注意一些什么事情呢,今天我们来看一篇关于MySQL数据库表分区注意事项的细节. 1.分区列索引约束 若表有primary key或unique key,则分区表的分区列必须包含在primary key或unique key列表里,这是为了确保主键的效率,否则同一主键区的东西一个在A分区,一个在B分区,显然会比较麻烦. 2.各分区类型条件 range 每个分区包含那些分区表达式的值位于一个给定的连续区间内的行.这些区间要连续且不能相互重叠 li

随机推荐