zoukankan      html  css  js  c++  java
  • linux下C++ STL hash_map的使用以及使用char *型变量作为Key值的一大“坑”

      计算机编程中经常会用到hash表,而在C++中,使用STL编程更是少不了的。本文将介绍STL中hash_map的使用、在hash_map中使用自定义类型作为key值的方法以及在使用char *类型作为key值时遇到的问题。
     
    一、需要的头文件以及命名空间
      在linux下使用STL hash_map除了需要引用其所在头文件<hash_map>之外还要引用其命名空间。像这样写
       1 using namespace __gnu_cxx; 
     
    二、hash_map的定义
      先来看看hash_map是怎么定义的:
      
    1  template<class _Key, class _Tp, class _HashFn = hash<_Key>,
    2        class _EqualKey = equal_to<_Key>, class _Alloc = allocator<_Tp> >
    3     class hash_map
    4     {
    5     //内容定义
    6     }

      其中的_Key当然是你使用的hash的关键字。用它可以唯一确定一个hash节点。_Tp是hash中存放的节点内容的类。_HashFn是hash_map的散列函数,默认采用hash<Key>这个模版函数,后面会详细说明。_EqualKey是hash的匹配函数,缺省使用系统定义的equal_to, 会在后面详细说明。_Alloc是容器的空间配置器,空间配置器一般不需要自己指定,也不赞成自己指定的空间配置器。这不是本文讨论的重点内容,在这篇文章中有我实现仿造源代码实现了一个空间配置其,里面注释很详细,有兴趣可以看下:http://www.cnblogs.com/zxtp/p/4975888.html。

      如果你想要写使用hash_map容器,应该这样定义:

       1 hash_map<int, int> Hash; 

      当然,其中的_Key与_Tp参数也可以是你想要的任何类型,包括字符串、结构体、类等。只是需要你多做一些事情(后面会详细介绍)。hash_map中默认支持如下几种类型:

        char、char *、const char *、unsigned char、signed char、short、unsigned short、int、unsigned int、long、unsignd long。

      也就是说你使用这几种类型作为参数时,不需要做其它工作。

      在hash_map中进行插入有三种方式,都是把它封装成对象的形式。如下:

    1     Hash[10] = 100;
    2     Hash.insert(hash_map<int, int>::value_type(20, 200));
    3     Hash.insert(pair<int, int>(30, 300));

    三、在hash_map中常用的操作(以下的代码都是以创建的hash表为例)

      1)、在hash_map的查询操作,如下:

    1 hash_map<int, int>::iterator it;    //创建一个迭代器变量
    2 it = Hash.find(100);                //在表中查询Key值为100的节点  
    3 if(it != Hash.end())                //表示含有该元素,反之则没有
    4 {
    5     cout << "有该元素" << endl;
    6 }   

      2)、在hash_map中的操作还有很多,这里只说了查找,这是因为本文后面会用到查找。其它的操作,网上很多例子,这里就不再做重复工作了。

      

    四、如何使用其它自定义参数类型作为Key?

      如何在hash中使用其它类型作为Key的参数呢?在hash_map中,如果你想使用自己定义的类型作为hash的Key值,那就需要你去实现它的散列函数和匹配函数。散列函数和比较函数都是对运算符"()"的重载,但是重载的内容不一样。具体例子如下:

      页式内存管理中,如果需要得到某段内存中存放的内容,你需要两个量:一个是内存页数,一个是在该页的偏移量。假设这里为内存建立一个索要,就以这两个量来当作hash表的关键字,只要你给出这两个量我就能索引到内存的具体位置。我可以用如下的结构作为hash_map的Key值。

    1 struct stIndex
    2 {
    3 public:
    4     unsigned int uiPage;//内存页数
    5     unsigned int uiOffset;//在该页的偏移量
    6 };

    然后需要重载散列函数,注意这个格式是固定的,你必须这样写,或者写在定义一个类,封装在类里面。实现如下:

    1 //hash散列函数,重载"()"
    2 struct stHash
    3 {
    4     size_t operator() (const stIndex& key) const
    5     {
    6         return key.uiPage;//这里我只用了其中一个字段作为其散列依据
    7     }
    8 };

    这里的散列函数其实不是真正意义上的散列函数,因为在hash_map的后面,会做一次取模的运算。

    还需要重载比较函数。其格式和上面一样,也是固定的。

    1 //hash的Key值比对函数,重载"()"
    2 struct stEqualKey
    3 {
    4     bool operator()(const stKey& Key1, const stKey& Key2) const
    5     {
    6         return Key1.uiPage == Key2.uiPage
    7                 && Key1.uiOffset == Key2.uiOffset;
    8     }
    9 };

    准备工作完成了,现在可以使用自定义类型作为hash的key值了。只是需要显示地指定你重载的hash函数和比较函数。像下面这样定义:

     1 hash_map<stIndex, void *vpPointer, stHash, stEqualKey> Hash; 

    其它的操作都是一样的了,这里就不再赘述了。

    五、hash_map的一大“坑”!

      为什么说这是一大“坑”呢?请耐心往下看!

      在hash_map中,你可能会有如下的定义:

    hash_map<char *, string> Hash;//这里的内容使用string是为了区分出char *

      这样的定义是没有错的,前面也说过,hash_map支持char *类型作为Key值。但是使用的时候就会出现一些意想不到的问题。如下:

    1 char pszKey[] = "abc";
    2 string sValue = "cdefg";
    3 Hash[pszStr] = sValue;//插入到hash中,并且成插入了
    4 
    5 hash_map<char *, string >::iterator it;
    6 it = find("abc");//查找不到刚才插入的内容
    7 
    8 it = find(pszKey);//能够查找到刚才插入的内容

      为什么会出现这种情况呢?在hash_map的比较函数是这样实现的:

    1   template <class _Tp>
    2     struct equal_to : public binary_function<_Tp, _Tp, bool>
    3     {
    4       bool
    5       operator()(const _Tp& __x, const _Tp& __y) const
    6       { return __x == __y; }
    7     };

    这里我们可以看到,它的比较模版函数传入的类型是创建hash_map时传入的第一个参数,也就是说我们写

     1 hash_map<char *,string > Hash; 

    的时候就已经把这个参数定义为了一个char *型的地址,在传入重载的函数中传入的只是一个地址,也就是char *型的参数,在比较的时候,比较的也只是地址而已。所以 it = find("abc"); 这样查找是查找不到的。如果在程序中一定要使用char *作为Key时,只有重载比较函数。而这个在STL中并没用进行说明。也许STL的本意是更本不支持地址类型。

  • 相关阅读:
    Asp.Net Web API 2第八课——Web API 2中的属性路由
    Asp.Net Web API 2第七课——Web API异常处理
    Asp.Net Web API 2第六课——Web API路由和动作选择
    Asp.Net Web API 2第五课——Web API路由
    开始学习python
    BMI 小程序 购物车
    深浅copy 文件操作
    字典 dict 集合set
    基本数据类型 (str,int,bool,tuple,)
    python 运算符
  • 原文地址:https://www.cnblogs.com/zxtp/p/5001356.html
Copyright © 2011-2022 走看看