linux下C++ STL hash_map的使用以及使用char *型变量作为Key值的一大“坑”
2015-12-02 21:24
771 查看
计算机编程中经常会用到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是怎么定义的:
其中的_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_map中常用的操作(以下的代码都是以创建的hash表为例)
1)、在hash_map的查询操作,如下:
2)、在hash_map中的操作还有很多,这里只说了查找,这是因为本文后面会用到查找。其它的操作,网上很多例子,这里就不再做重复工作了。
四、如何使用其它自定义参数类型作为Key?
如何在hash中使用其它类型作为Key的参数呢?在hash_map中,如果你想使用自己定义的类型作为hash的Key值,那就需要你去实现它的散列函数和匹配函数。散列函数和比较函数都是对运算符"()"的重载,但是重载的内容不一样。具体例子如下:
页式内存管理中,如果需要得到某段内存中存放的内容,你需要两个量:一个是内存页数,一个是在该页的偏移量。假设这里为内存建立一个索要,就以这两个量来当作hash表的关键字,只要你给出这两个量我就能索引到内存的具体位置。我可以用如下的结构作为hash_map的Key值。
然后需要重载散列函数,注意这个格式是固定的,你必须这样写,或者写在定义一个类,封装在类里面。实现如下:
这里的散列函数其实不是真正意义上的散列函数,因为在hash_map的后面,会做一次取模的运算。
还需要重载比较函数。其格式和上面一样,也是固定的。
准备工作完成了,现在可以使用自定义类型作为hash的key值了。只是需要显示地指定你重载的hash函数和比较函数。像下面这样定义:
hash_map<stIndex, void *vpPointer, stHash, stEqualKey> Hash;
其它的操作都是一样的了,这里就不再赘述了。
五、hash_map的一大“坑”!
为什么说这是一大“坑”呢?请耐心往下看!
在hash_map中,你可能会有如下的定义:
这样的定义是没有错的,前面也说过,hash_map支持char *类型作为Key值。但是使用的时候就会出现一些意想不到的问题。如下:
为什么会出现这种情况呢?在hash_map的比较函数是这样实现的:
这里我们可以看到,它的比较模版函数传入的类型是创建hash_map时传入的第一个参数,也就是说我们写
hash_map<char *,string > Hash;
的时候就已经把这个参数定义为了一个char *型的地址,在传入重载的函数中传入的只是一个地址,也就是char *型的参数,在比较的时候,比较的也只是地址而已。所以 it = find("abc"); 这样查找是查找不到的。如果在程序中一定要使用char *作为Key时,只有重载比较函数。而这个在STL中并没用进行说明。也许STL的本意是更本不支持地址类型。
一、需要的头文件以及命名空间
在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[10] = 100; Hash.insert(hash_map<int, int>::value_type(20, 200)); 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值。
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下SQLPLUS无法使用删除及上下键
- linux内存屏蔽技术
- LINUX 安全运维 (五)
- linux0.11 内核启动代码分析(二)
- Windows中查找命令的路径 (类似Linux中的which命令)
- linux 软件卸载
- Linux里类似批处理中的pause命令
- Linux下使用FreeTDS访问MS SQL Server 2005数据库(包含C测试源码)
- windows复制文件到Linux或Windows,有关FTP的配置与使用
- 由一个线程例子引发的思考(转载)
- Centos7.0 修改防火墙为iptables
- linux学习(三)如何在linux系统下利用vi编辑C/C++程序
- Linux学习笔记(6)之vi编辑器基本操作
- Linux下Rsync+sersync实现数据双向实时同步
- 如何建立linux下的 LNMP 环境 + VSFTPD
- 详解CentOS设置163的yum源的过程
- Linux学习笔记(4)之用户、用户组及权限管理
- Linux系统CentOS使用yum方式安装指定版本的PHP 添加yum源 从PHP5.3升级到5.4/5.5/5.6
- Linux学习笔记(4)
- python 实现 Centos 7.1 下系统自动分区并mount到/data*