zoukankan      html  css  js  c++  java
  • 使用C++STL的map容器实现一种命令映射

    因为最近在练习写一个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#double类型转换string类型
    数据分析测试
    第三周进度
    质量属性战术——可用性战术
    开学第二周进度报告
    开学第一周进度报告
    质量属性的六个常见属性场景
    架构漫谈有感03
    架构漫谈有感02
    读架构漫谈有感--软件架构师如何工作
  • 原文地址:https://www.cnblogs.com/sigma0-/p/12630460.html
Copyright © 2011-2022 走看看