背景
在开发过程中,经常遇到需要在不同的场景下调用不同的函数以实现不同的功能(例如消息分发),传统的设计方法中大量的使用 if/else,switch/case结构。如下所示:
假设你需要一个可以返回每个月中天数的函数(为简单起见不考虑闰年),一个比较笨的方法是一个大的if语句:
int iGetMonthDays(int iMonth)
{
int iDays;
if(1 == iMonth) {iDays = 31;}
else if(2 == iMonth) {iDays = 28;}
else if(3 == iMonth) {iDays = 31;}
else if(4 == iMonth) {iDays = 30;}
else if(5 == iMonth) {iDays = 31;}
else if(6 == iMonth) {iDays = 30;}
else if(7 == iMonth) {iDays = 31;}
else if(8 == iMonth) {iDays = 31;}
else if(9 == iMonth) {iDays = 30;}
else if(10 == iMonth) {iDays = 31;}
else if(11 == iMonth) {iDays = 30;}
else if(12 == iMonth) {iDays = 31;}
return iDays;
}
可以看出本来应该很简单的一件事情,代码却是这么冗余,对于 if/else,switch/case结构的实现方式有两个缺点:
- 分支越多,可读性越差,维护起来也越麻烦
- if/else,switch/case结构对于分支的个数也有一定的限制,可扩展性不强。
所以解决这个的办法就可以用表驱动方法:表驱动方法是一种使可以在表中查找信息,而不必用很多的逻辑语句(if或Case)来把它们找出来的方法。事实上,任何信息都可以通过表来挑选。在简单的情况下,逻辑语句往往更简单而且更直接。但随着逻辑链的复杂,表就变得越来越富有吸引力了。
基础知识
在使用表驱动方法时需要了解三个核心问题:
- 什么是表?
- 将在表中存储些什么?
- 怎样从表中查询条目?
什么是表?
在c/c++中很多都可以作为表,比如数组、哈希表(key-value)等,我们可以选择哈希表,因为在哈希表的查询效率很高。
将在表中存储些什么?怎样从表中查询条目?
在某些情况下,表查寻的结果是数据,如果是这种情况,你可以把数据存储在表中;在其它情况下,表查寻的结果是动作,在这种情况下,你可以把描述这一动作的代码存储在表中;在某些语言中,也可以把实现这一动作的子程序的调用存储在表中,也就是将函数的指针保存在表中,当查找到这项时,让程序用这个函数指针来调用相应的程序代码,即回调函数,这个就是函数指针在表驱动方法中的应用。
具体使用
// CPacket 为数据包
typedef int(CCommand::*CMDFUNC)(std::list<CPacket>&, CPacket& inPacket); // 回调函数
std::map<int, CMDFUNC> m_mapFunction; // 哈希表:从命令号到功能映射
struct
{
int nCmd;
CMDFUNC func;
}data[] = {
{1, &CCommand::MakeDirverInfo},
{2, &CCommand::MakeDirectoryInfor},
{3, &CCommand::RunFile},
{4, &CCommand::DownloadFile},
{5, &CCommand::MouseEvent},
{6, &CCommand::SendScreen},
{7, &CCommand::LockMachine},
{8, &CCommand::UnLockMachine},
{9, &CCommand::DeleteLocalFile},
{1981, &CCommand::TestConnect},
{-1, NULL},
};
// 添加
for (int i = 0; data[i].nCmd != -1; i++)
{
m_mapFunction.insert(std::pair<int, CMDFUNC>(data[i].nCmd, data[i].func));
}
// 使用
std::map<int, CMDFUNC>::iterator it = m_mapFunction.find(nCmd);
if (it == m_mapFunction.end())
{
return -1;
}
总结
扩展个数:switch case最多是256个,而哈希可以无限扩展。
效率比较:switch底层实现大量使用汇编的if else(所以效率比代码if else高),如果case个数非常多,效率会急剧降低;而哈希的效率在选择项少时,效率没有if else、switch case高,但随着个数的增加,优势会越来越明显。
所以如果选择项比较少的时候使用if 和switch效率更高,更合适;但是如果当选择项比较多时使用哈希表查询的效率更高,更合适。