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

  template<class _Key, class _Tp, class _HashFn = hash<_Key>,
class _EqualKey = equal_to<_Key>, class _Alloc = allocator<_Tp> >
class hash_map
{
//内容定义
}

  其中的_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容器,应该这样定义:

    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中进行插入有三种方式,都是把它封装成对象的形式。如下:

     Hash[] = ;
Hash.insert(hash_map<int, int>::value_type(, ));
Hash.insert(pair<int, int>(, ));

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

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

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

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

  

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

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

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

 struct stIndex
{
public:
unsigned int uiPage;//内存页数
unsigned int uiOffset;//在该页的偏移量
};

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

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

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

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

 //hash的Key值比对函数,重载"()"
struct stEqualKey
{
bool operator()(const stKey& Key1, const stKey& Key2) const
{
return Key1.uiPage == Key2.uiPage
&& Key1.uiOffset == Key2.uiOffset;
}
};

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

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

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

五、hash_map的一大“坑”!

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

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

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

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

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

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

   template <class _Tp>
struct equal_to : public binary_function<_Tp, _Tp, bool>
{
bool
operator()(const _Tp& __x, const _Tp& __y) const
{ return __x == __y; }
};

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

hash_map<char *,string > Hash;

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

linux下C++ STL hash_map的使用以及使用char *型变量作为Key值的一大“坑”的更多相关文章

  1. 在阿里云linux下使用SVN访问VisualSVN出错:SSL handshake failed: SSL error: Key usage violation in certificate has been detected

    Subversion clients receive the following error message when attempting to connect to VisualSVN Serve ...

  2. Linux下的Shell编程

    从程序员的角度来看, Shell本身是一种用C语言编写的程序,从用户的角度来看,Shell是用户与Linux操作系统沟通的桥梁.用户既可以输入命令执行,又可以利用 Shell脚本编程,完成更加复杂的操 ...

  3. Linux下C程序的编辑,编译和运行以及调试

    国内私募机构九鼎控股打造APP,来就送 20元现金领取地址:http://jdb.jiudingcapital.com/phone.html 内部邀请码:C8E245J (不写邀请码,没有现金送) 国 ...

  4. linux下JDK,tomcat的安装与环境变量配置

    先从网上下载jdk(jdk-1_5_0_02-linux-i586.rpm) ,下载后放在/home目录中,当然其它地方也行. 进入安装目录#cd /home#cp jdk-1_5_0_02-linu ...

  5. socket在windows下和linux下的区别

    原文:socket在windows下和linux下的区别 1)头文件 windows下winsock.h/winsock2.h linux下sys/socket.h    错误处理:errno.h 2 ...

  6. Linux 下V4l2摄像头采集图片,实现yuyv转RGB,RGB转BMP,RGB伸缩,jpeglib 库实现压缩RGB到内存中,JPEG经UDP发送功(转)

    ./configure CC=arm-linux-gnueabihf-gcc LD=arm-linux-gnueabihf-ld --host=arm-linux --prefix=/usr/loca ...

  7. Linux下onvif客户端获取h265 IPC摄像头的RTSP地址

    1. 设备搜索,去获取webserver 的地址 ,目的是在获取能力提供服务地址,demo:https://www.cnblogs.com/croxd/p/10683429.html 2. GetCa ...

  8. 重要:Linux下IDE--KDevelop (用来跟踪调试C++) Ubuntu下QT4开发环境的搭建及初体验

    Linux下安装Qt4有两大问题,一是环境变量,二是IDE(集成开发环境).安装Qt4也有两种方法,一种是apt-get,一种是下载源码包,而后 一种方法已经人证实是最有可能不好使的方法.所以我最终采 ...

  9. Linux下的Shell编程(2)环境变量和局部变量

    Shell Script是一种弱类型语言,使用变量的时候无需首先声明其类型. 局部变量在本地数据区分配内存进行存储,这个变量归当前的Shell所有,任何子进 程都不能访问本地变量.这些变量与环境变量不 ...

随机推荐

  1. jQuery UI Datepicker&Datetimepicker添加 时-分-秒 并且,判断

    jQuery UI Datepicker时间(年-月-日) 相关代码: <input type="text" value="" name="ad ...

  2. Ubuntu Codeblocks Installation

    Download and Installation sudo add-apt-repository ppa:damien-moore/codeblocks sudo apt update sudo a ...

  3. iOS开发多线程-多线程技术1

    一.基本概念 1.什么是进程 进程就是指在系统中正在运行的一个应用程序 每个应用之间是相互独立的 每个进程都运行在其专有的并且受保护的内存空间内. 2.什么是线程 一个进程想要执行程序,就必须需要一个 ...

  4. Oracle的几种索引

    Oracle 提供了多种不同类型的索引以供使用.简单地说, Oracle 中包括如下索引: 1. B* 树索引 这些是我所说的 “ 传统 “ 索引.到目前为止,这是 Oracle 和大多数其他数据库中 ...

  5. poj 3318 Matrix Multiplication

    http://poj.org/problem?id=3318 矩阵A*矩阵B是否等于矩阵C #include <cstdio> #include <cstring> #incl ...

  6. C语言必背18个经典程序

    C语言必背18个经典程序 1./*输出9*9口诀.共9行9列,i控制行,j控制列.*/ #include "stdio.h" main() {int i,j,result; for ...

  7. T-SQL 基于列的逻辑表达式 (CASE)

    CASE简介 基于列的逻辑表达式,其实就是CASE表达式.可以用在SELECT,UPDATE,DELETE,SET以及IN,WHERE,ORDER BY和HAVING子句之后.由于这里讲的是T-SQL ...

  8. < IOS > X-code 5.1 x86 - 64 编译问题

    关于xcode 5.1   x86 - 64 编译问题   坐等了N久,终于IOS 7.1 发布了,作为一个果粉,忍不住第一时间升级了.结果用设备测试的时候,出问题了,一直检测不到设备,哈哈,纠结了半 ...

  9. 给ie浏览器添加推荐浏览器提示

    <script type="text/javascript"> var isIE = !!window.ActiveXObject; var isIE6 = isIE ...

  10. uva 1589 by sixleaves

    坑爹的模拟题目.自己对于这种比较复杂点得模拟题的能力概述还不够,还多加练习.贴别是做得时候一直再想如何检查车中间有没有棋子,炮中间有没有棋子.到网上参考别人的代码才发先这么简单的办法,自己尽然想不到. ...