STL中的map和hashmap数据结构

STL中map的数据结构

1.map定义

map是键-值对的集合。map类型通常可以理解为关联数组:可使用键作为下标来获取一个值,正如内置数组类型一样。而关联的本质在于元素的值与某个特定的键相关联,而并非通过元素在数组中的位置来获取。

<1>map模板原型:

template < class Key, class T, class Compare = less<Key>,          

      class Allocator = allocator<pair<const Key,T> > > class map;

key:关键值的类型。在map对象中的每个元素是通过该关键值唯一确定元素的。 

T:映射值的类型。在map中的每个元素是用来储存一些数据作为其映射值。 

compare:Comparison类:A类键的类型,它有两个参数,并返回一个bool。表达comp(A,B),comp是这比较类A和B是关键值的对象,应返回true,如果是在早先的立场比B放置在一个严格弱排序操作。这可以是一个类实现一个函数调用运算符或一个函数的指针(见一个例子构造)。默认的对于<KEY>,返回申请小于操作符相同的默认值(A <B)。  Map对象使用这个表达式来确定在容器中元素的位置。以下这个规则在任何时候都排列在map容器中的所有元素。 

Allocator:用于定义存储分配模型分配器对象的类型。默认情况下,分配器类模板,它定义了最简单的内存分配模式,是值独立的

<2>map模板参数

map<Key, Data, Compare, Alloc>

<3>map的详细用法可参考:http://blog.csdn.net/bat603/article/details/1456141

2.map的实现机制

C++ STL 之所以得到广泛的赞誉,也被很多人使用,不只是提供了像vector, string, list等方便的容器,更重要的是STL封装了许多复杂的数据结构算法和大量常用数据结构操作。vector封装数组,list封装了链表,map和 set封装了二叉树等,在封装这些数据结构的时候,STL按照程序员的使用习惯,以成员函数方式提供的常用操作,如:插入、排序、删除、查找等。让用户在 STL使用过程中,并不会感到陌生。

C++ STL中标准关联容器set, multiset, map, multimap内部采用的就是一种非常高效的平衡检索二叉树:红黑树,也成为RB树(Red-Black Tree)。RB树的统计性能要好于一般的平衡二叉树(有些书籍根据作者姓名,Adelson-Velskii和Landis,将其称为AVL-树),所以被STL选择作为了关联容器的内部结构。本文并不会介绍详细AVL树和RB树的实现以及他们的优劣,关于RB树的详细实现参看红黑树: 理论与实现(理论篇)。本文针对开始提出的几个问题的回答,来向大家简单介绍map和set的底层数据结构。

<1>为何map和set的插入删除效率比用其他序列容器高?

之所以效率高,是因为对于关联容器来说,不需要做内存拷贝和内存移动。map和set容器内所有元素都是以节点的方式来存储,其节点结构和链表差不多,指向父节点和子节点。结构图可能如下:

?

    A

   / \

  B   C

/ \ / \

D E F  G

因此插入的时候只需要稍做变换,把节点的指针指向新的节点就可以了。删除的时候类似,稍做变换后把指向删除节点的指针指向其他节点就OK了。这里的一切操作就是指针换来换去,和内存移动没有关系。

<2>为何每次insert之后,以前保存的iterator不会失效?

看见了上面答案的解释,你应该已经可以很容易解释这个问题。iterator这里就相当于指向节点的指针,内存没有变,指向内存的指针怎么会失效呢(当然 被删除的那个元素本身已经失效了)。相对于vector来说,每一次删除和插入,指针都有可能失效,调用push_back在尾部插入也是如此。因为为了保证内部数据的连续存放,iterator指向的那块内存在删除和插入过程中可能已经被其他内存覆盖或者内存已经被释放了。即使时push_back的时 候,容器内部空间可能不够,需要一块新的更大的内存,只有把以前的内存释放,申请新的更大的内存,复制已有的数据元素到新的内存,最后把需要插入的元素放 到最后,那么以前的内存指针自然就不可用了。特别时在和find等算法在一起使用的时候,牢记这个原则:不要使用过期的iterator。

<3>为何map和set不能像vector一样有个reserve函数来预分配数据?

究其原理来说时,引起它的原因在于在map和set内部存储的已经不是元素本身了,而是包含元素的节点。也就是说map内部使用的Alloc并不是map<Key, Data, Compare, Alloc>声明的时候从参数中传入的Alloc。例如:

map<int, int, less<int>, Alloc<int> > intmap;

这时候在intmap中使用的allocator并不是Alloc<int>, 而是通过了转换的Alloc,具体转换的方法时在内部通过Alloc<int>::rebind重新定义了新的节点分配器,详细的实现参看彻底学习STL中的Allocator。其实你就记住一点,在map和set内面的分配器已经发生了变化,reserve方法你就不要奢望了。

<4>当数据元素增多时(10000和20000个比较),map和set的插入和搜索速度变化如何?

在map和set中查找是使用二分查找,也就是说,如果有16个元素,最多需要比较4次就能找到结 果,有32个元素,最多比较5次。那么有10000个呢?最多比较的次数为log10000,最多为14次,如果是20000个元素呢?最多不过15次。 看见了吧,当数据量增大一倍的时候,搜索次数只不过多了1次,多了1/14的搜索时间而已。你明白这个道理后,就可以安心往里面放入元素了。

最后,对于map和set Winter还要提的就是它们和一个c语言包装库的效率比较。在许多unix和linux平台下,都有一个库叫isc,里面就提供类似于以下声明的函数:

?

void tree_init(void **tree);

void *tree_srch(void **tree, int (*compare)(), void *data);

void tree_add(void **tree, int (*compare)(), void *data, void (*del_uar)());

int tree_delete(void **tree, int (*compare)(), void *data,void (*del_uar)());

int tree_trav(void **tree, int (*trav_uar)());

void tree_mung(void **tree, void (*del_uar)());

许多人认为直接使用这些函数会比STL map速度快,因为STL map中使用了许多模板什么的。其实不然,它们的区别并不在于算法,而在于内存碎片。如果直接使用这些函数,你需要自己去new一些节点,当节点特别多, 而且进行频繁的删除和插入的时候,内存碎片就会存在,而STL采用自己的Allocator分配内存,以内存池的方式来管理这些内存,会大大减少内存碎 片,从而会提升系统的整体性能。本文原作者在自己的系统中做过测试,把以前所有直接用isc函数的代码替换成map,程序速度基本一致。当时间运行很长时间后(例如后台服务程序),map的优势就会体现出来。从另外一个方面讲,使用map会大大降低你的编码难度,同时增加程序的可读性。何乐而不为?

 

 

1 数据结构:hash_map原理

这是一节让你深入理解hash_map的介绍,如果你只是想囫囵吞枣,不想理解其原理,你倒是可以略过这一节,但我还是建议你看看,多了解一些没有坏处。

hash_map基于hash table(哈希表)。 哈希表最大的优点,就是把数据的存储和查找消耗的时间大大降低,几乎可以看成是常数时间;而代价仅仅是消耗比较多的内存。然而在当前可利用内存越来越多的情况下,用空间换时间的做法是值得的。另外,编码比较容易也是它的特点之一。

其基本原理是:使用一个下标范围比较大的数组来存储元素。可以设计一个函数(哈希函数,也叫做散列函数),使得每个元素的关键字都与一个函数值(即数组下标,hash值)相对应,于是用这个数组单元来存储这个元素;也可以简单的理解为,按照关键字为每一个元素“分类”,然后将这个元素存储在相应“类”所对应的地方,称为桶。

但是,不能够保证每个元素的关键字与函数值是一一对应的,因此极有可能出现对于不同的元素,却计算出了相同的函数值,这样就产生了“冲突”,换句话说,就是把不同的元素分在了相同的“类”之中。 总的来说,“直接定址”与“解决冲突”是哈希表的两大特点。

hash_map,首先分配一大片内存,形成许多桶。是利用hash函数,对key进行映射到不同区域(桶)进行保存。其插入过程是:

  1. 得到key
  2. 通过hash函数得到hash值
  3. 得到桶号(一般都为hash值对桶数求模)
  4. 存放key和value在桶内。

其取值过程是:

  1. 得到key
  2. 通过hash函数得到hash值
  3. 得到桶号(一般都为hash值对桶数求模)
  4. 比较桶的内部元素是否与key相等,若都不相等,则没有找到。
  5. 取出相等的记录的value。

hash_map中直接地址用hash函数生成,解决冲突,用比较函数解决。这里可以看出,如果每个桶内部只有一个元素,那么查找的时候只有一次比较。当许多桶内没有值时,许多查询就会更快了(指查不到的时候).

由此可见,要实现哈希表, 和用户相关的是:hash函数和比较函数。这两个参数刚好是我们在使用hash_map时需要指定的参数。

2 hash_map 使用

2.1 一个简单实例

不要着急如何把"岳不群"用hash_map表示,我们先看一个简单的例子:随机给你一个ID号和ID号相应的信息,ID号的范围是1~2的31次方。如何快速保存查找。

#include <hash_map>
#include <string>
using namespace std;
int main(){
        hash_map<int, string> mymap;
        mymap[9527]="唐伯虎点秋香";
        mymap[1000000]="百万富翁的生活";
        mymap[10000]="白领的工资底线";
        ...
        if(mymap.find(10000) != mymap.end()){
                ...
        }

够简单,和map使用方法一样。这时你或许会问?hash函数和比较函数呢?不是要指定么?你说对了,但是在你没有指定hash函数和比较函数的时候,你会有一个缺省的函数,看看hash_map的声明,你会更加明白。下面是SGI STL的声明:

template <class _Key, class _Tp, class _HashFcn = hash<_Key>,
class _EqualKey = equal_to<_Key>,
class _Alloc = __STL_DEFAULT_ALLOCATOR(_Tp) >
class hash_map
{
        ...
}

也就是说,在上例中,有以下等同关系:

...
hash_map<int, string> mymap;
//等同于:
hash_map<int, string, hash<int>, equal_to<int> > mymap;

Alloc我们就不要取关注太多了(希望深入了解Allocator的朋友可以参看标准库 STL :Allocator能做什么)

2.2 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 char*>
struct hash<char> 
struct hash<unsigned char> 
struct hash<signed char>
struct hash<short>
struct hash<unsigned short> 
struct hash<int> 
struct hash<unsigned int>
struct hash<long> 
struct hash<unsigned long> 

也就是说,如果你的key使用的是以上类型中的一种,你都可以使用缺省的hash函数。当然你自己也可以定义自己的hash函数。对于自定义变量,你只能如此,例如对于string,就必须自定义hash函数。例如:

struct str_hash{
        size_t operator()(const string& str) const
        {
                unsigned long __h = 0;
                for (size_t i = 0 ; i < str.size() ; i ++)
                __h = 5*__h + str[i];
                return size_t(__h);
        }
};
//如果你希望利用系统定义的字符串hash函数,你可以这样写:
struct str_hash{
        size_t operator()(const string& str) const
        {
                return return __stl_hash_string(str.c_str());
        }
};

在声明自己的哈希函数时要注意以下几点:

  1. 使用struct,然后重载operator().
  2. 返回是size_t
  3. 参数是你要hash的key的类型。
  4. 函数是const类型的。

如果这些比较难记,最简单的方法就是照猫画虎,找一个函数改改就是了。

现在可以对开头的"岳不群"进行哈希化了 smile . 直接替换成下面的声明即可:

map<string, string> namemap; 
//改为:
hash_map<string, string, str_hash> namemap;

其他用法都不用边。当然不要忘了吧str_hash的声明以及头文件改为hash_map。

你或许会问:比较函数呢?别着急,这里就开始介绍hash_map中的比较函数。

2.3 hash_map 的比较函数

在map中的比较函数,需要提供less函数。如果没有提供,缺省的也是less< Key> 。在hash_map中,要比较桶内的数据和key是否相等,因此需要的是是否等于的函数:equal_to< Key> 。先看看equal_to的源码:

//本代码可以从SGI STL
//先看看binary_function 函数声明,其实只是定义一些类型而已。
template <class _Arg1, class _Arg2, class _Result>
struct binary_function {
        typedef _Arg1 first_argument_type;
        typedef _Arg2 second_argument_type;
        typedef _Result result_type;
};
//看看equal_to的定义:
template <class _Tp>
struct equal_to : public binary_function<_Tp,_Tp,bool>
{
        bool operator()(const _Tp& __x, const _Tp& __y) const { return __x == __y; }
};

如果你使用一个自定义的数据类型,如struct mystruct, 或者const char* 的字符串,如何使用比较函数?使用比较函数,有两种方法. 第一种是:重载==操作符,利用equal_to;看看下面的例子:

struct mystruct{
        int iID;
        int  len;
        bool operator==(const mystruct & my) const{
                return (iID==my.iID) && (len==my.len) ;
        }
};  

这样,就可以使用equal_to< mystruct>作为比较函数了。另一种方法就是使用函数对象。自定义一个比较函数体:

struct compare_str{
        bool operator()(const char* p1, const char*p2) const{
                return strcmp(p1,p2)==0;
        }
};  

有了compare_str,就可以使用hash_map了。

typedef hash_map<const char*, string, hash<const char*>, compare_str> StrIntMap;
StrIntMap namemap;
namemap["岳不群"]="华山派掌门人,人称君子剑";
namemap["张三丰"]="武当掌门人,太极拳创始人";
namemap["东方不败"]="第一高手,葵花宝典";

2.4 hash_map 函数

hash_map的函数和map的函数差不多。具体函数的参数和解释,请参看:STL 编程手册:Hash_map,这里主要介绍几个常用函数。

  1. hash_map(size_type n) 如果讲究效率,这个参数是必须要设置的。n 主要用来设置hash_map 容器中hash桶的个数。桶个数越多,hash函数发生冲突的概率就越小,重新申请内存的概率就越小。n越大,效率越高,但是内存消耗也越大。
  2. const_iterator find(const key_type& k) const. 用查找,输入为键值,返回为迭代器。
  3. data_type& operator[](const key_type& k) . 这是我最常用的一个函数。因为其特别方便,可像使用数组一样使用。不过需要注意的是,当你使用[key ]操作符时,如果容器中没有key元素,这就相当于自动增加了一个key元素。因此当你只是想知道容器中是否有key元素时,你可以使用find。如果你希望插入该元素时,你可以直接使用[]操作符。
  4. insert 函数。在容器中不包含key值时,insert函数和[]操作符的功能差不多。但是当容器中元素越来越多,每个桶中的元素会增加,为了保证效率,hash_map会自动申请更大的内存,以生成更多的桶。因此在insert以后,以前的iterator有可能是不可用的。
  5. erase 函数。在insert的过程中,当每个桶的元素太多时,hash_map可能会自动扩充容器的内存。但在sgi stl中是erase并不自动回收内存。因此你调用erase后,其他元素的iterator还是可用的。

3 相关hash容器

hash 容器除了hash_map之外,还有hash_set, hash_multimap, has_multiset, 这些容器使用起来和set, multimap, multiset的区别与hash_map和map的区别一样,我想不需要我一一细说了吧。

4 其他

这里列几个常见问题,应该对你理解和使用hash_map比较有帮助。

4.1 hash_map和map的区别在哪里?

  • 构造函数。hash_map需要hash函数,等于函数;map只需要比较函数(小于函数).
  • 存储结构。hash_map采用hash表存储,map一般采用红黑树(RB Tree)实现。因此其memory数据结构是不一样的。

4.2 什么时候需要用hash_map,什么时候需要用map?

总体来说,hash_map 查找速度会比map快,而且查找速度基本和数据数据量大小,属于常数级别;而map的查找速度是log(n)级别。并不一定常数就比log(n)小,hash还有hash函数的耗时,明白了吧,如果你考虑效率,特别是在元素达到一定数量级时,考虑考虑hash_map。但若你对内存使用特别严格,希望程序尽可能少消耗内存,那么一定要小心,hash_map可能会让你陷入尴尬,特别是当你的hash_map对象特别多时,你就更无法控制了,而且hash_map的构造速度较慢。

现在知道如何选择了吗?权衡三个因素: 查找速度, 数据量, 内存使用。

这里还有个关于hash_map和map的小故事,看看:http://dev.csdn.net/Develop/article/14/14019.shtm

4.3 如何在hash_map中加入自己定义的类型?

你只要做两件事, 定义hash函数,定义等于比较函数。下面的代码是一个例子:

-bash-2.05b$ cat my.cpp
#include <hash_map>
#include <string>
#include <iostream>

using namespace std;
//define the class
class ClassA{
        public:
        ClassA(int a):c_a(a){}
        int getvalue()const { return c_a;}
        void setvalue(int a){c_a;}
        private:
        int c_a;
};

//1 define the hash function
struct hash_A{
        size_t operator()(const class ClassA & A)const{
                //  return  hash<int>(classA.getvalue());
                return A.getvalue();
        }
};

//2 define the equal function
struct equal_A{
        bool operator()(const class ClassA & a1, const class ClassA & a2)const{
                return  a1.getvalue() == a2.getvalue();
        }
};

int main()
{
        hash_map<ClassA, string, hash_A, equal_A> hmap;
        ClassA a1(12);
        hmap[a1]="I am 12";
        ClassA a2(198877);
        hmap[a2]="I am 198877";
        
        cout<<hmap[a1]<<endl;
        cout<<hmap[a2]<<endl;
        return 0;
}
-bash-2.05b$ make my
c++  -O -pipe -march=pentiumpro  my.cpp  -o my
-bash-2.05b$ ./my
I am 12
I am 198877

typedef map<Key, Value> KeyMap;

当你希望使用hash_map来替换的时候,只需要修改:

typedef hash_map<Key, Value> KeyMap;


其他的基本不变。当然,你需要注意是否有Key类型的hash函数和比较函数。

//////////////////////////////////////

参数使用说明

来源http://www.stlchina.org/twiki/bin/view.pl/Main/STLHashMap

/////////////////////////////////////////////

hash_map<Key, Data, HashFcn, EqualKey, Alloc>

Category: containers

Description

Hash_map 是一种使用hash 关联容器,把Key 和value数据对应存储; Hash_map 同样是一个Pair 的关联容器,这意味着其元素类型是pair<const Key, Data>; Hash_map 还是Unique 关联容器,即使用EqualKey比较函数来判断不存在两个元素的key值相等。

由于hash_map在通过key值查找时具有很高的效率,所以hash_map对于一些互不相干的元素的存储非常有用。如果元素的某种顺序比较重要,使用map更合适一些。

Example

struct eqstr
{
        bool operator()(const char* s1, const char* s2) const
        {
                return strcmp(s1, s2) == 0;
        }
};

int main()
{
        hash_map<const char*, int, hash<const char*>, eqstr> months;
        
        months["january"] = 31;
        months["february"] = 28;
        months["march"] = 31;
        months["april"] = 30;
        months["may"] = 31;
        months["june"] = 30;
        months["july"] = 31;
        months["august"] = 31;
        months["september"] = 30;
        months["october"] = 31;
        months["november"] = 30;
        months["december"] = 31;
        
        cout << "september -> " << months["september"] << endl;
        cout << "april     -> " << months["april"] << endl;
        cout << "june      -> " << months["june"] << endl;
        cout << "november  -> " << months["november"] << endl;
}

Definition Defined in the header hash_map, and in the backward-compatibility header hash_map.h. This class is an SGI extension; it is not part of the C++ standard.

Template parameters

Parameter Description

Default
Key The hash_map's key type. This is also defined as hash_map::key_type.  

Data The hash_map's data type. This is also defined as hash_map::data_type.  

HashFcn The hash function used by the hash_map. This is also defined as hash_map::hasher. hash<Key>

EqualKey The hash_map key equality function: a binary predicate that determines whether two keys are equal. This is also defined as hash_map::key_equal. equal_to<Key>

Alloc The hash_map's allocator, used for all internal memory management. alloc

Members

Member Where defined Description

key_type Associative Container The hash_map's key type, Key.

data_type Pair Associative Container The type of object associated with the keys.

value_type Pair Associative Container The type of object, pair<const key_type, data_type>, stored in the hash_map.

hasher Hashed Associative Container The hash_map's hash function.

key_equal Hashed Associative Container Function object that compares keys for equality.

pointer Container Pointer to T.

reference Container Reference to T

const_reference

Container Const reference to T size_type

Container An unsigned integral type. difference_type Container

A signed integral type. iterator Container Iterator used to iterate through a hash_map. [1]

const_iterator Container Const iterator used to iterate through a hash_map.

iterator begin() Container Returns an iterator pointing to the beginning of the hash_map.

iterator end() Container Returns an iterator pointing to the end of the hash_map.

const_iterator begin() const Container Returns an const_iterator pointing to the beginning of the hash_map.

const_iterator end() const Container Returns an const_iterator pointing to the end of the hash_map.

size_type size() const Container Returns the size of the hash_map.

size_type max_size() const Container Returns the largest possible size of the hash_map.

bool empty() const Container true if the hash_map's size is 0.

size_type bucket_count() const Hashed Associative Container Returns the number of buckets used by the hash_map.

void resize(size_type n) Hashed Associative Container Increases the bucket count to at least n.

hasher hash_funct() const Hashed Associative Container Returns the hasher object used by the hash_map.

key_equal key_eq() const Hashed Associative Container Returns the key_equal object used by the hash_map.

hash_map() Container Creates an empty hash_map.

hash_map(size_type n) Hashed Associative Container Creates an empty hash_map with at least n buckets.

hash_map(size_type n, 
         const hasher& h)
Hashed Associative Container Creates an empty hash_map with at least n buckets, using h

as the hash function.

hash_map(size_type n,
         const hasher& h, 
         const key_equal& k)
Hashed Associative Container

Creates an empty hash_map with at least n buckets, using h as the hash function and k as the key equal function.

template <class InputIterator>
hash_map(InputIterator f, InputIterator l)
[2]
Unique Hashed Associative Container

Creates a hash_map with a copy of a range.
template <class InputIterator>
hash_map(InputIterator f, InputIterator l,
         size_type n)
[2]

Unique Hashed Associative Container Creates a hash_map with a copy of a range and a bucket count of at least n.
template <class InputIterator>

hash_map(InputIterator f, InputIterator l,
         size_type n, const hasher& h)
[2]
Unique Hashed Associative Container Creates a hash_map with a copy of a range and a bucket count of at least n, using h as the hash function.

template <class InputIterator>
hash_map(InputIterator f, InputIterator l,
         size_type n, const hasher& h, 
         const key_equal& k)
[2]

Unique Hashed Associative Container Creates a hash_map with a copy of a range and a bucket count of at least n, using h as the hash function and k as the key equal function.

hash_map(const hash_map&) Container The copy constructor.

hash_map& operator=(const hash_map&) Container The assignment operator

void swap(hash_map&) Container Swaps the contents of two hash_maps.
pair<iterator, bool>
insert(const value_type& x)
Unique Associative Container Inserts x into the hash_map.

template <class InputIterator>
void insert(InputIterator f, InputIterator l)
[2]
Unique Associative Container

Inserts a range into the hash_map. void erase(iterator pos) Associative Container

Erases the element pointed to by pos. size_type erase(const key_type& k) Associative Container

Erases the element whose key is k. void erase(iterator first, iterator last) Associative Container

Erases all elements in a range. void clear() Associative Container Erases all of the elements.

const_iterator find(const key_type& k) const Associative Container Finds an element whose key is k.

iterator find(const key_type& k) Associative Container Finds an element whose key is k.

size_type count(const key_type& k) const Unique Associative Container Counts the number of elements whose key is k.

pair<const_iterator, const_iterator>
equal_range(const key_type& k) const
Associative Container

Finds a range containing all elements whose key is k.
pair<iterator, iterator>
equal_range(const key_type& k)

Associative Container Finds a range containing all elements whose key is k.
data_type& 

operator[](const key_type& k) [3]
hash_map See below.

bool operator==(const hash_map&, 
                const hash_map&)
Hashed Associative Container Tests two hash_maps for equality. This is a global function, not a member function.

New members

These members are not defined in the Unique Hashed Associative Container and Pair Associative Container requirements, but are specific to hash_map.

Member Description

data_type& 
operator[](const key_type& k) [3]
Returns a reference to the object that is associated with a particular key. If the hash_map does not already contain such an object, operator[] inserts the default object data_type(). [3]

Notes

[1] Hash_map::iterator is not a mutable iterator, because hash_map::value_type is not Assignable. That is, if i is of type hash_map::iterator and p is of type

hash_map::value_type, then *i = p is not a valid expression. However, hash_map::iterator isn't a constant iterator either, because it can be used to modify the object that it points to. Using the same notation as above, (*i).second = p is a valid expression.

[2] This member function relies on member template functions, which at present (early 1998) are not supported by all compilers. If your compiler supports member templates, you can call this function with any type of input iterator. If your compiler does not yet support member templates, though, then the arguments must either be of type const value_type* or of type

hash_map::const_iterator.

[3] Since operator[] might insert a new element into the hash_map, it can't possibly be a const member function. Note that the definition of operator[] is extremely simple: m[k] is equivalent to

(*((m.insert(value_type(k, data_type()))).first)).second. Strictly speaking, this member function is unnecessary: it exists only for convenience.

 

展开阅读全文

没有更多推荐了,返回首页