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

编辑: admin 分类: c#语言 发布时间: 2022-03-15 来源:互联网
目录
  • 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++表驱动法内容请搜索海外IDC网以前的文章或继续浏览下面的相关文章希望大家以后多多支持海外IDC网!