c++ map key值不为基本类型的比较

STL中的map底层是用红黑树实现的,其泛型原型如下:

template <class _Key, class _Tp, class _Compare, class _Alloc>
class map {
              ......
              }

其中_Key表示比较的键(key),_Tp表示值(value),_Compare表示比较方式,_Alloc表示内存分配器。

一般我们在写map的时候总是类似于写出如下代码:

map<int, char*>* my_map = new map<int, char*>;

表示键为int类型,值为字符串类型。这里之所以不对_Compare和_Alloc加以限制,是因为int是C++内置类型,有默认比较方式,_Alloc也采用STL的

默认的内存方案。但是如果有如下结构体:

struct Term{
    char* str;
    int   hashCode;
};

现在我们要将该Term作为map的键,并假设Term所对应的值为Term出现的频率(int型),那么能不能这样写:

map<Term, int>* my_map = new map<Term, int>;

显然这样写map是无法正常运作的,原因是struct Term并非C++的内置类型,默认不知道如何去比较它。

这时候就需要修改map的默认比较方式:

一、定义map时修改比较规则

template <class T>
struct Compare
{
  int operator()(const T& x, const T& k) const{
    if(x.hashCode >= k.hashCode) return 0;
    else return 1;
  }
};

这里采用的是函数对象(function object)的方式去加载map的比较方式,表示使用Term的hashCode作为比较方式,以对红黑树进行查找、插入等操作。

这样我们就可以把map写成下面的形式:

map<Term, int, Compare<Term> >* my_map = new map<Term, int, Compare<Term> >;

这样map就可以正常运作了,比如进行插入操作:

Term my_term;
my_map->insert(make_pair(my_term, 1));

但是上面的struct Compare为什么要写成这样的形式,写成这样行不行:

template <class T>
struct Compare
{
  int operator()(const T& x, const T& k) const{
    if(x.hashCode >= k.hashCode) return 1;
    else return 0;
  }
};

这是不行的。为什么不行,首先来看一看map中find的源代码:

template <class _Key, class _Value, class _KeyOfValue, 
          class _Compare, class _Alloc>
typename _Rb_tree<_Key,_Value,_KeyOfValue,_Compare,_Alloc>::iterator 
_Rb_tree<_Key,_Value,_KeyOfValue,_Compare,_Alloc>::find(const _Key& __k)
{
  _Link_type __y = _M_header;      // Last node which is not less than __k. 
  _Link_type __x = _M_root();      // Current node. 

  while (__x != 0) 
    if (!_M_key_compare(_S_key(__x), __k))
      __y = __x, __x = _S_left(__x);
    else
      __x = _S_right(__x);

  iterator __j = iterator(__y);   
  return (__j == end() || _M_key_compare(__k, _S_key(__j._M_node))) ? end() : __j;
}

上面的代码中_M_key_compare就表示我们的那个比较函数对象,_S_key(__x)表示取__x节点的key,并和__k比较。

if (!_M_key_compare(_S_key(__x), __k))
      __y = __x, __x = _S_left(__x);

表示如果_S_key(__x) >= __k即,如果节点的key大于或等于查找的key那么就__x就等于它的左子节点,否则就为右子节点。

但为什么等于的时候不直接返回呢,却在继续查找?举个例子来说:

如果我们要查找key为10的节点是否在树中时,首先从根节点开始查找,由于8<10,这时_M_key_compare返回1,那么此时,

转向root的右子树,然后由于10==10,_M_key_compare返回0,这时转向左子树,但左子树是空的,循环停止。

 return (__j == end() || _M_key_compare(__k, _S_key(__j._M_node))) ? end() : __j;

由于此时__j表示"10"这个节点(其实是个迭代器),由于__k为10,而__j._M_node的key为10,_M_key_compare返回0,有三元运算符可知,

此时返回是__j,即表示找到了。因此我们的比较函数对象必需写成:

当节点键大于等于所要查找或插入的键时,返回0(false),反之为1(true),这是由内部源代码所决定的。

二、key值类实现“<”符号重载

#include<iostream>
#include<map>
#include<string>

typedef struct test_map{

void test(void);
        test_map(std::string sa,std::string sb,int a,int b)
{
        _sa = sa;
        _sb = sb;
        _a = a;
        _b = b;
}

#if 1
bool operator < (const test_map & test1) const
 {
        return _sa < test1._sa;

 }
#endif
#if 0
bool operator == (const test_map & test1) const
 {
        return _sa == test1._sa;

 }
#endif
//private: 
   std::string _sa;
   std::string _sb;
   int _a;
   int _b;


}t_m;
#if 0
template <class T>
struct Compare
{
  int operator()(const T& x, const T& k) const{
    if(x._sa >= k._sa) return 0;
    else return 1;
  }
};
#endif

int main(int argc,char* argv[])
{
        std::map<t_m,int> test;
        //std::map<t_m,int,Compare<t_m> > test;
        t_m ts1("aaa","bbb",1,1);
        t_m ts2("ccc","bbb",1,1);
        t_m ts3("ddd","bbb",1,1);
        t_m ts4("eee","bbb",1,1);
        t_m ts5("fff","bbb",1,1);
        test.insert(std::make_pair(ts1,1));
        test.insert(std::make_pair(ts2,1));
        test.insert(std::make_pair(ts3,1));
        test.insert(std::make_pair(ts4,1));
        //test.insert(std::make_pair(ts5,1));   

        if(test.find(ts1) != test.end())
                std::cout<<"find ts1 in test"<<std::endl;

        if(test.find(ts5) != test.end())
                std::cout<<"find ts5 in test"<<std::endl;
        else
                std::cout<<"no find ts5 in test"<<std::endl;

        return 0;
}
参考:https://www.cnblogs.com/zjfdlut/archive/2011/08/12/2135698.html

猜你喜欢

转载自blog.csdn.net/swj9099/article/details/89450557
今日推荐