C++ STL hash_map的使用以及STL hash_map的大“坑”

  计算机编程中经常会用到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的本意是更本不支持地址类型。

时间: 2024-10-13 22:23:55

C++ STL hash_map的使用以及STL hash_map的大“坑”的相关文章

【STL】帮你复习STL泛型算法 一

STL泛型算法 #include <iostream> #include <vector> #include <algorithm> #include <iterator> #include <numeric> #include <list> using std::cout; using std::endl; using std::vector; using std::list; bool IsOushu(const int&

STL学习第一章 了解STL

知识内容: 1.STL介绍 2.C++基础知识复习 3.C++中的模板简单介绍 4.STL组成部分 一.STL介绍 1.什么是STL? 学过C++的应该都听说过STL,那么什么是STL呢?STL是Standard Template Library的简称,翻译为标准模板库,是惠普实验室开发的一系列软件的统称.它是由Alexander Stepanov.Meng Lee和David R Musser在惠普实验室工作时所开发出来的.从根本上说,STL是一些"容器"的集合,这些"容器

几道STL题目(FJUT - OJ STL训练1)

这个OJ一直在做,一些专题题目都很好,从易至难,阶梯上升,很适合像我这样的蒟蒻 =7= 这篇是关于其中一个专题训练的题解思路及代码   http://120.78.128.11/Contest.jsp?cid=486 所有题面我就不贴了,各位自行去看,链接在上一行 =7= 一.求众数(Map标记+Set) 其实数组维护也可以做,但既然是STL训练,就用STL的东西了 用map标记数据和出现的次数,然后转入结构体存入map中 为什么不直接用set<map<T,T> >的形式 是因为m

C++ STL中的哈希表 hash_map

在定义hash_map容器的时候,不仅需要指定键和值的类型,还需要指定hash函数和相等函数 (一)hash_map 的hash函数 hash< int>到底是什么样子?看看源码: struct hash<int> { size_t operator()(int __x) const { return __x; } }; 原来是个函数对象.在SGI STL中,提供了以下hash函数: struct hash<char*> struct hash<const cha

【STL源码学习】STL算法学习之四

排序算法是STL算法中相当常用的一个类别,包括部分排序和全部排序算法,依据效率和应用场景进行选择. 明细: sort 函数原型: template <class RandomAccessIterator> void sort (RandomAccessIterator first, RandomAccessIterator last); template <class RandomAccessIterator, class Compare> void sort (RandomAcc

【STL源码学习】STL算法学习之三

第一章:前言 数量不多,用到的时候会很爽. 第二章:明细 STL算法中的又一个分类:分割:将已有元素按照既定规则分割成两部分.  is_partitioned 函数原型: template <class InputIterator, class UnaryPredicate> bool is_partitioned (InputIterator first, InputIterator last, UnaryPredicate pred); 函数作用: 如果序列被分为两部分,前一部分pred都

【STL源码学习】STL算法学习之二

第一章:前言 学习笔记,记录学习STL算法的一些个人所得,在以后想用的时候可以快速拾起. 第二章:明细 copy 函数原型: template <class InputIterator, class OutputIterator> OutputIterator copy (InputIterator first, InputIterator last, OutputIterator result); 函数作用: 将[first,last)区间的元素拷贝至result开头的迭代器区间,并返回赋值

【STL源码学习】STL算法学习之一

第一章:引子 STL包含的算法头文件有三个:<algorithm><numeric><functional>,其中最大最常用的是<algorithm>,今天学习的是<algorithm>包含的算法中的第一部分:非修改顺序操作算法. 接下来学习的算法基于C++11标准,较老的IDE会支持不全面或者部分算法不支持. 第二章:原型解析 如分类名称体现的信息,本节的所有函数都不会修改序列,并且原理上都是顺序遍历迭代器实现的. all_of 函数原型: t

信STL,得永生(STL大法好)

STL大法好! 当然,是不可能得永生的,但它可以延长你的在役时间 最近总结了一些STL的神奇用法,C++的STL和作弊器一样,简直kongbu...... 所以说,我们一起来看看STL的用法吧! 目录: 1.vector 2.stack 3.queue&priority_queue 4.string 5.map 6.set&multiset 7.list 8.pair 9.bitset 10.algorithm 当然,这只会是一个大致的讲解,不会非常详细 vector vector,就是不