因为最近在练习写一个ftp的服务器,其中的命令有很多种,每个命令对应一个执行函数,能够想到的最简单的实现方式便是使用if……else匹配命令和执行对应的函数,如下所示:

if(strcmp("one",cmd) == 0)
{
……
}
else if(……)
{
……
}

为了避免频繁地使用if……else……,一种方法是可以建立一个命令和函数指针的数组。大致可以如下实现:

//函数指针
typedef void (*CMD_HANDLER)(int arg);
//命令和函数指针的数组
typedef struct cmd
{
const char *cmd;
CMD_HANDLER cmd_handler;
} cmd_t;
//命令映射的实现
static cmd_t cmds[] = {
// 命令 函数指针
{"one", do_one },
{"two", do_two },
……
……
};

然后遍历该数组,匹配命令然后执行函数

//遍历命令和函数指针的数组
for (i = 0; i < size; ++i)
{
if (strcmp(cmds[i].cmd,要执行的命令) == 0)//命令匹配
{
if (cmds[i].cmd_handler != NULL)
{//不为空则执行该函数
cmds[i].cmd_handler(参数);
break;
}
}
……
……
}

这种方法避免了频繁使用if……else……语句,但是每次查找命令时都需要遍历查找和匹配,似乎效率也不是很高。

为了提高效率,可以使用哈希表或者关联容器来实现,STL里面的map和hash_map效率都是比较高的,函数指针的方式或许可以用boost::functiond代替,不过我这里实现的还是基于函数指针的。

STL的map底层是用红黑树实现的,查找时间复杂度是log(n);

STL的hash_map底层是用hash表存储的,查询时间复杂度是O(1);

那么什么时候用map,什么时候用hash_map呢?

这个要看具体的应用,不一定常数级别的hash_map一定比log(n)级别的map要好,hash_map的hash函数以及解决地址冲突等都要耗时间,而且众所周知hash表是以空间换时间的,因而hash_map的内存消耗肯定要大,一般情况下,如果记录非常大,考虑hash_map,查找效率会高很多,如果要考虑内存消耗,则要谨慎使用hash_map。因为ftp的命令虽然多,但是也只是几十个而已,所以这里我是使用map实现。

首先定义一个类

class commandsfunc
{
public:
typedef void (commandsfunc::*cmd_handler)(int arg);//函数指针,带有一个参数
void do_func();
private:
map<const char*,cmd_handler> commandmap;
map<const char*,cmd_handler>::iterator it;
void do_one(int arg);
void do_two(int arg);
void do_three(int arg);
};

typedef void (commandsfunc::*cmd_handler)(int arg);是函数指针,为什么是这样的呢?,因为我这里是要在commandsfunc类中调用commandsfunc类自己的回调函数。

do_func是在main函数中调用的,这里只是一个示例而已,在往map容器中装入几个值之后,查找one命令对应的函数。在实际的使用中,应该把命令和函数指针全部装载进map中,然后在map中查找由从别处发来的命令,执行相应的回调函数。

/**
*do_func - 主要的执行函数
*/
void commandsfunc::do_func()
{
commandmap["one"] = &commandsfunc::do_one;
commandmap["two"] = &commandsfunc::do_two;
commandmap["three"] = &commandsfunc::do_three; it = commandmap.find("one");//根据key查找value
if(it != commandmap.end())
{//存在和命令相对应的函数
(this->*(it->second))(100);//执行该类自身的成员函数,it->second为函数地址,100为参数
}
else
{
cout<<"Unknow command."<<endl;
}
}

commandmap[“one”] = &commandsfunc::do_one;这里map的key one 对应的value是一个函数的地址,因为是使用类的成员函数,需要使用commandsfunc::,如果不使用类中的成员函数,则函数指针的声明可以如下:

  typedef void (*cmd_handler)(int arg);

相应的,这里value的值可以这么赋值

commandmap["one"] = do_one;

map容器里面存放有内容后,使用函数find查找,这种查找的效率是相对较高的,查找到了之后使用下面的语句可以执行对应的函数

(this->*(it->second))(100);

it是map容器的一个迭代器,他的第二个成员it->second就是map的value的值,即是类中函数的地址,*(it->second)则表示类中的函数,由于是调用自身类的成员函数,再使用this指针,100是参数。貌似如果是使用类的成员函数指针都得这样用。如果这里没有使用map,而是单纯地使用对象成员函数的指针,则也是用::* 来定义指向类成员函数的指针->*来调用函数。

//函数指针
typedef void (commandsfunc::*cmd_handler)(int arg);
//定义一个函数指针,可作为参数
cmd_handler actOp;
//那么可以这么调用
(this->*actOp)(100);//调用指定的函数

整体的测试程序如下:

(这里只是查找一个试试而已)

/**
*start from the very beginning,and to create greatness
*@author: LinChuangwei
*@E-mail:979951191@qq.com
*@brief:用map实现命令映射的一个小测试
*/ #include <iostream>
#include <map>
#include <string.h>
using namespace std; class commandsfunc
{
public:
typedef void (commandsfunc::*cmd_handler)(int arg);//函数指针,带有一个参数
void do_func();
private:
map<const char*,cmd_handler> commandmap;
map<const char*,cmd_handler>::iterator it;
void do_one(int arg);
void do_two(int arg);
void do_three(int arg);
};
/**
*do_func - 主要的执行函数
*/
void commandsfunc::do_func()
{
commandmap["one"] = &commandsfunc::do_one;
commandmap["two"] = &commandsfunc::do_two;
commandmap["three"] = &commandsfunc::do_three; it = commandmap.find("one");//根据key查找value
if(it != commandmap.end())
{//存在和命令相对应的函数
(this->*(it->second))(100);//执行该类自身的成员函数,it->second为函数地址,100为参数
}
else
{
cout<<"Unknow command."<<endl;
}
}
/**
*do_XXX - 几个回调函数的实现
*/
void commandsfunc::do_one(int arg)
{
std::cout<<"one:"<<arg<<std::endl;
} void commandsfunc::do_two(int arg)
{
std::cout<<"two:"<<arg<<std::endl;
} void commandsfunc::do_three(int arg)
{
std::cout<<"three:"<<arg<<std::endl;
}
/*
*main - 主函数
*/
int main(int argc, char const *argv[])
{
commandsfunc lcw;
lcw.do_func();
return 0;
}

使用下面的命令编译

g++ -o command_map command_map.cpp

执行结果如下:



说明这种方法可以成功,hash_map的以后再试了。

使用C++STL的map容器实现一种命令映射的更多相关文章

  1. C++ STL 中 map 容器

    C++ STL 中 map 容器 Map是STL的一个关联容器,它提供一对一(其中第一个可以称为关键字,每个关键字只能在map中出现一次,第二个可能称为该关键字的值)的数据 处理能力,由于这个特性,它 ...

  2. stl之map容器的原理及应用

    容器的数据结构同样是采用红黑树进行管理,插入的元素健位不允许重复,所使用的节点元素的比较函数,只对元素的健值进行比较,元素的各项数据可通过健值检索出来.map容器是一种关联容器,实现了SortedAs ...

  3. STL之map容器的详解

    一.关于map的介绍 map是STL的 一个容器,和set一样,map也是一种关联式容器.它提供一对一(其中第一个可以称为关键字,每个关键字只能在map中出现一次,第二个可能称为该关键 字的值)的数据 ...

  4. C++STL中map容器的说明和使用技巧(杂谈)

    1.map简介 map是一类关联式容器.它的特点是增加和删除节点对迭代器的影响很小,除了那个操作节点,对其他的节点都没有什么影响.对于迭代器来说,可以修改实值,而不能修改key. 2.map的功能 自 ...

  5. STL的map容器将第3个模板参数设为less_equal或greater_equal会怎样?

    最近都在学Linux系统编程,用C就足矣,有段时间没碰C++了,于是实现些算法练手. 实现多项式乘法的时候发现有几项没有合并同类项,最终调试到这一步时发现了问题. res是map类型,用find查找k ...

  6. STL——序列式容器

    一.容器概述与分类 1. STL容器即是将运用最广的一些数据结构实现出来.常用的数据结构有array, list, tree, stack, queue, hash table, set, map…… ...

  7. c++ map容器使用及问题

    C++ STL库map容器一些总结,欢迎大家指正补充. map容器由两部分组成,分别为关键字(Key)和值(Value),关键字和值都可以声明为任意类型的数据,注意:关键字唯一,不能重复!使用需包含头 ...

  8. STL中map与hash_map容器的选择收藏

    这篇文章来自我今天碰到的一个问题,一个朋友问我使用map和hash_map的效率问题,虽然我也了解一些,但是我不敢直接告诉朋友,因为我怕我说错了,通过我查询一些帖子,我这里做一个总结!内容分别来自al ...

  9. STL --> map容器

    map容器 一.map简介 map是一类关联式容器.它的特点是增加和删除节点对迭代器的影响很小,除了那个操作节点,对其他的节点都没有什么影响.对于迭代器来说,可以修改实值,而不能修改key. 二.ma ...

随机推荐

  1. 2017蓝桥杯日期问题(C++B组)

    标题:日期问题小明正在整理一批历史文献.这些历史文献中出现了很多日期.小明知道这些日期都在1960年1月1日至2059年12月31日.令小明头疼的是,这些日期采用的格式非常不统一,有采用年/月/日的, ...

  2. css怪异盒模型和弹性盒布局(flex)详解及其案例

    一.怪异盒模型 怪异盒模型的属性是box-sizing,他有两个属性值: 1.content-box 这是由 CSS2.1 规定的宽度高度行为.宽度和高度分别应用到元素的内容框.在宽度和高度之外绘制元 ...

  3. Array(数组)对象-->slice() 方法

    1.定义和用法 slice()方法可提取字符串的某个部分,并以新的字符串返回被提取的部分. 语法: array.slice(start, end) 参数:start 开始元素的下标,截取内容包含该元素 ...

  4. matplotlib 中其他基于Affine2DBase的类

    2020-04-11 11:42:22 --Edit by yangray以下所有类(除了BboxTransformToMaxOnly )都直接继承于Affine2DBaseIdentityTrans ...

  5. Dijkstra学习总结

    啥叫堆 可以看一下这个 https://www.cnblogs.com/xiugeng/p/9645972.html#_label0普通Dijkstra可以看一下 https://blog.csdn. ...

  6. std::string::insert函数

    string& insert (size_t pos, const string& str); string& insert (size_t pos, const string ...

  7. .NET Core技术研究-主机

    前一段时间,和大家分享了 ASP.NET Core技术研究-探秘Host主机启动过程 但是没有深入说明主机的设计.今天整理了一下主机的一些知识,结合先前的博文,完整地介绍一下.NET Core的主机的 ...

  8. RNN循环神经网络(Recurrent Neural Network)学习

    一.RNN简介 1.)什么是RNN? RNN是一种特殊的神经网络结构,考虑前一时刻的输入,且赋予了网络对前面的内容的一种'记忆'功能. 2.)RNN可以解决什么问题? 时间先后顺序的问题都可以使用RN ...

  9. Obtain The String CodeForces - 1295C binary_search+思维

    妈耶,,,被B题卡到哭,C题一发就过了... 字符串问题.首先用vector记录每个字符出现的位置,然后对字符串t的每个字符,用二分查找函数查找,注意用upper_bound查找,对于字符i,首先用变 ...

  10. C语言二维数组超细讲解

    用一维数组处理二维表格,实际是可行的,但是会很复杂,特别是遇到二维表格的输入.处理和输出. 在你绞尽脑汁的时候,二维数组(一维数组的大哥)像电视剧里救美的英雄一样显现在你的面前,初识数组的朋友们还等什 ...