C++11: unordered_map实例化时传参的两种方式

【免责声明】本人乃才疏学浅之辈,此抛砖引玉之举;若有纰漏错误之处,望不吝指正。

【原文链接】C++11: unordered maps By Ernesto Bascón Pantoja's blog

【内容提要】

        1、unordered maps简介
        2、unordered_map实例化时,传参的两种方式

【正文】

       C++ STL提供了一个有序map关联容器(模板类),其内部实现是一种高效的平衡二叉树;这种实现的优点是检索效率高(平均时间复杂度O(log2N),N表示map中元素个数),并且按照已经排列好的次序遍历map容器查找元素。

       C++11增加了一种无序map容器,其内部实现是哈希表。这种实现的显著优点是访问容器中任意元素时间复杂度为常量级(O(1)),但其明显的不足是容器中的元素无序且需要更大的内存空间(与map相比)。

      The API of both kind of maps is quite similar; for example, look at this code:

     上述 两种map容器的API非常相似,如下列示例代码所示:

#include <iostream>
#include <string>
#include <map>
#include <unordered_map>
 
using namespace std;
 
template <class T>
void show(const T& x)
{
    for (const auto& i : x)
        cout << i.first << "; " << i.second << endl;
}
 
template <template <class...> class T>
void test()
{
    T<string, int> x;
    x.insert(make_pair("one", 1));
    x.insert(make_pair("two", 2));
    x.insert(make_pair("three", 3));
    x.insert(make_pair("four", 4));
     
    show(x);
}
 
 
 
int main()
{
  test<map>();
  cout << "*******************" << endl;
  test<unordered_map>();
}

上面程序代码输出如下:


four; 4
one; 1
three; 3
two; 2
*******************
one; 1
three; 3
two; 2
four; 4

        如你所见:

  • 两种容器创建和操作使用的方式完全一致。
  •  unordered_map容器的输出是无序的。

       如果只是使用STL提供的基本类型(int ,char,long,short,etc)和strings,一切都很美好(STL已经提供默认的hash code和compare函数)。一旦我们想使用自定义的数据类型:类(class)或结构体(struct)时,使用unordered_map就不太舒适了。

      我们知道,在JAVA中,如果类要作为容器的键(Key),需要重写(override)类的equals 和hashCode方法,否则代码能正常编译,却难如期工作。(假设你已经有java知识)

      在C++中,我们也需要做类似的事情,比如,1、需要实现一个函数对象hash(function object,对应于下面代码片段中的hash<KEY>参数)用于生成哈希值(hash code),2、同时需要实现一个函数对象equal_to(对应于下面代码中的equal_to<K>参数)用于比较自定义类或结构体的两个对象,3、实例化unordered map容器时,传入前两步构造的函数对象作为模板参数。具体代码如下:

template <typename KEY, typename VALUE, typename HASH = hash<KEY>, typename EQUAL = equal_to<K> >
class unordered_map
{ ..... };
        实例化unordered map容器时,需要实现的hash函数对象的模板如下代码所示:

template <typename T>
class hash
{
    public:
        long operator()(const T& o) const { return 0; }
};

and provides specializations for all the primitive types and for the std::string class.

The equal_to class template is implemented similar to this:

template <typename T>
class equal_to
{
  public:
    bool operator()(const T& a, const T& b) const { return a == b; }
};
        因此,如果我们勇于KEY的自定义类或结构体重写了operator==操作符,我们可以不需提供EQUAL模板参数(equal_to函数对象)。

        现以自定义结构体(struct article_id)和自定义类(class article)为例,实现如下:

struct article_id
{
  string brand;
  int id;
   
  article_id(const string& b, int id) : brand(b), id(id) { }
};
 
ostream& operator<<(ostream& os, const article_id& a)
{
  return os << "(" << a.brand << ") " << a.id;
}
 
class article
{
  string brand, model, description;
 
  public:
    article(const string& b, const string& m, const string& d)
       : brand(b), model(m), description(d) { }
 
    const string& get_brand() const { return brand; }
    const string& get_model() const { return model; }
    const string& get_description() const { return description; }
};
 
ostream& operator<<(ostream& os, const article& a)
{
  return os << "Brand: " << a.get_brand() << "; Model: " << a.get_model()
         << "; Description: " << a.get_description();
}
      如果用unordered  map存储,用自定义结构体(article_id)为Key,自定义类article的实例为值(<article _id,article object>)键值对,我们需要做如下工作,1、实现hash函数对象,2、实现equal _to函数对象,代码如下:
class article_hash
{
  public:
    long operator()(const article_id& x) const
    {
        hash<string> z;
        return z(x.brand) * 100 + x.id;
    }
};
 
class article_equal_to
{
  public:
     bool operator()(const article_id& a, const article_id& b) const
     {
        return a.brand == b.brand && a.id == b.id;
     }
};

       定义了hash和equal_to函数对象后,我们就可以用自定义结构体article_id做unordered map函数的Key.代码如下:

int main()
{
   unordered_map<article_id, article, article_hash, article_equal_to> x;
   x.insert(make_pair(article_id("acme", 1), article("acme", "tv", "Acme TV")));
   x.insert(make_pair(article_id("foo", 4), article("foo", "phone", "fooPhone")));
   show(x);
   return 0;
}
      到此,我们了解了实例化unordered map容器的第一种传参方式,即实例化容器时,显示传入自己实现的函数对象(hash  equal_to )模板参数;

      另外一种更成熟的方式是,用自定义结构体或类的article_hash and article_equal_to 函数对象,“重写“(specializations) 命名空间std中的hash和equal_to模板。

namespace std
{
    template <>
    class hash<article_id>
    {
      public:
        long operator()(const article_id& x) const
        {
            hash<string> z;
            return z(x.brand) * 100 + x.id;
        }
    };
     
    template <>
    class equal_to<article_id>
    {
      public:
         bool operator()(const article_id& a, const article_id& b) const
         {
            return a.brand == b.brand && a.id == b.id;
         }
    };
}

        当我们用第二种方式,重写(特化(specializations))命名空间std中的hash和equal_to 模板后,实例化 unordered map容器时,就无需显示传入参数3,与参数4(typename HASH =hash <KEY>,typename EQUAL=equal_to  <K>)

int main()
{
   unordered_map<article_id, article> x;
   x.insert(make_pair(article_id("acme", 1), article("acme", "tv", "Acme TV")));
   x.insert(make_pair(article_id("foo", 4), article("foo", "phone", "fooPhone")));
   show(x);
   return 0;
}

REALLY COOL.OVER:).

更多链接:

http://cpptutorials.freeforums.org/tr1-unordered-map-t621.html

http://stackoverflow.com/questions/15595804/c-hash-function-for-string-in-unordered-map

http://stackoverflow.com/questions/15601568/need-example-of-stdunordered-map-constructor




                       

         


  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值