以查找表取代switch...case

转自:http://blog.csdn.net/hjsunj/article/details/2028597

传统的以多态取代switch...case

Martin Flower的著作《重构》告诉我们应该以多态取代switch...case,比如一个经典的Shape绘图的例子:


void Draw( Shape* shape ) {
    switch( shape.type ) {
        case line: dynamic_cast< Line* >( shape )->Draw(); break;
        case rect: dynamic_cast< Rect* >( shape )->Draw(); break;
    }
}

使用多态则变为:

void Draw( Shape* shape ) {
    shape->Draw();
}

注意前者的Draw为非虚函数,后者则为虚函数。

多态无法实施

可是并非switch...case总是处理多态对象,比如面对一个消息ID。
此时switch...case也并非如想象的那么遭糕,至少在其case条目比较少的情况下能够工作的很好。
比如下面这段处理消息的代码:

void FunctionA( Message* message );
...
void FunctionN( Message* message );

void HandleMessage( Message* message ) {
    switch ( message->GetID() ) {
        case MessageID_A:
            FunctionA( message );
            break;
        ...
        case MessageID_N:
            FunctionN( message );
            break;
        default:
            assert( false && "Unknown message" );
    }
}

switch...case效率低下

可是当消息变得越来越多时,switch...case的效率将成为问题,因为它执行的是个线性查找过程。
比如我们要查找的case条目正是最好一条,switch...case则会将比较前面的每一条case,此过程跟if..else if..else类似。

曙光:使用查找表

要提高查找速度,一个方法是使用查找表(你可以使用map查找或者对排序的vector做二分查找)。
第一步要做的就是做出一个Lookup函数,给一个ID返回一个值(函数指针或者其他),以封装switch...case过程。

可能的实现如下:

typedef void (*MsgFunc)( Message* message );

MsgFunc Lookup( Message* message ) {
    typedef map<int, MsgFunc> Registry;
    struct RegistryInitializer {
        const Registry& operator()() {
            static Registry registry;
            registry[MessageID_A] = &FunctionA;
            ...
            registry[MessageID_N] = &FunctionN;
            return registry;
        }
    };

    // 使用局部static实现惰性初始化
    static const Registry& registry = RegistryInitializer()();
    Registry::const_iterator it = registry.find( i );
    if ( it != registry.end() ) {
        return it->second;
    }
    else {
        assert( false && "Unknown message id" );
        return NULL;
    }
}

然后HandleMessage使用Lookup搜寻出处理函数并调用之。

void HandleMessage( Message* message ) {
    MsgFunc msgFunc = Lookup( message->GetID() );
    if ( msgFunc != NULL ) {
        (*msgFunc)();
    }   
    else {
        assert( false && "Unknown message" );
    }
}

可以看出使用查找表不但效率高,还可以将映射集中初始化,每次有新的条目增加进来,只需要简单的加一句

registry[MessageID_X] = &FunctionX;

至少比switch...case增加条目是少打几个字。

结论

在条目较少的情况时使用switch...case依然是个不错的选择,因为作出一个查找表并非是个轻松的工作;如果条目很多,那么用查找表吧!

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值