C++进阶——STL源码之map与multimap

14 篇文章 4 订阅

STL源码之map与multimap

map

map的特性是,所有元素都会根据元素的键值自动被排序;
map的所有元素都是pair,同时拥有实值(value)和 键值 (key),pair的第一个元素视为键值,第二个元素视为实值;
map不允许两个元素具有相同的键值。

map的迭代器可以用来修改元素的实值(value),修改元素的实值并不会影响map的排列规则;
map的迭代器不可以用来改变map的键值,因为修改map的键值会严重破坏map的结构。

map对其中的元素进行删除或者新增操作,操作之前的迭代器,在操作完成之后依然有效,但是被操作的元素除外。

map的底层结构为rb-tree。

pair的定义

template<class _Ty1,
	class _Ty2>
struct pair{	// store a pair of values
	typedef pair<_Ty1, _Ty2> _Myt;
	typedef _Ty1 first_type;
	typedef _Ty2 second_type;

	template<class _Uty1 = _Ty1,
		class _Uty2 = _Ty2,
		class = enable_if_t<is_default_constructible<_Uty1>::value
						&& is_default_constructible<_Uty2>::value> >
		constexpr pair()
		: first(), second()
		{	// default construct
		}

	_Ty1 first;		// the first stored value
	_Ty2 second;	// the second stored value

}

map与multimap的区别

  • map和multimap的key为key,value为key+data,与set是不同的,set的key就是value,value就是key。
  • map的key不可修改,map与multimap的插入调用函数不同,影响了其key是否对应value。
  • map有[]操作符,而multimap没有[]操作符。

map的结构

      map的红黑树的定义为:typedef _Rb_tree<key_type, value_type, _Select1st<value_type>,key_compare, _Pair_alloc_type> _Rep_type;可以看出 value_type是一个pair定义为 typedef std::pair<const _Key, _Tp>   value_type; 其中 pair的第一个值为key,并且是const,第二个值为value。 

_Select1st是一个仿函数,在stl_function中可以找到源码如下:

template<typename _Pair>
struct _Select1st
: public unary_function<_Pair, typename _Pair::first_type>
{
    typename _Pair::first_type&
    operator()(_Pair& __x) const
    { return __x.first; }
};
  template <typename _Key, typename _Tp, typename _Compare = std::less<_Key>,
            typename _Alloc = std::allocator<std::pair<const _Key, _Tp> > >
    class map
    {
    public:
      typedef _Key                                          key_type;
      typedef _Tp                                           mapped_type;
      typedef std::pair<const _Key, _Tp>                    value_type;
      typedef _Compare                                      key_compare;
      typedef _Alloc                                        allocator_type;

    private:
      // concept requirements
      typedef typename _Alloc::value_type                   _Alloc_value_type;
      __glibcxx_class_requires(_Tp, _SGIAssignableConcept)
      __glibcxx_class_requires4(_Compare, bool, _Key, _Key,
				_BinaryFunctionConcept)
      __glibcxx_class_requires2(value_type, _Alloc_value_type, _SameTypeConcept)

    public:
      class value_compare
      : public std::binary_function<value_type, value_type, bool>
      {
	friend class map<_Key, _Tp, _Compare, _Alloc>;
      protected:
	_Compare comp;

	value_compare(_Compare __c)
	: comp(__c) { }

      public:
	bool operator()(const value_type& __x, const value_type& __y) const
	{ return comp(__x.first, __y.first); }
      };

    private:
      /// This turns a red-black tree into a [multi]map. 
      typedef typename __gnu_cxx::__alloc_traits<_Alloc>::template
	rebind<value_type>::other _Pair_alloc_type;

      typedef _Rb_tree<key_type, value_type, _Select1st<value_type>,
		       key_compare, _Pair_alloc_type> _Rep_type;

      /// The actual tree structure.
      _Rep_type _M_t;

      typedef __gnu_cxx::__alloc_traits<_Pair_alloc_type> _Alloc_traits;

    public:
      // many of these are specified differently in ISO, but the following are
      // "functionally equivalent"
      typedef typename _Alloc_traits::pointer            pointer;
      typedef typename _Alloc_traits::const_pointer      const_pointer;
      typedef typename _Alloc_traits::reference          reference;
      typedef typename _Alloc_traits::const_reference    const_reference;
      typedef typename _Rep_type::iterator               iterator;
      typedef typename _Rep_type::const_iterator         const_iterator;
      typedef typename _Rep_type::size_type              size_type;
      typedef typename _Rep_type::difference_type        difference_type;
      typedef typename _Rep_type::reverse_iterator       reverse_iterator;
      typedef typename _Rep_type::const_reverse_iterator const_reverse_iterator;

      // [23.3.1.1] construct/copy/destroy
      // (get_allocator() is also listed in this section)

      /**
       *  @brief  Default constructor creates no elements.
       */
      map()
      : _M_t() { }

      ...

}

map的insert操作

这里需要说明的是,insert的返回类型是一个pair,由一个迭代器和一个bool组成,后者bool的值表示当前是否插入成功;成功的话,迭代器指向插入的元素;如果失败则迭代器返回插入失败点的原始元素;将在【】操作符中利用该性质。

      std::pair<iterator, bool>
      insert(const value_type& __x)
      { return _M_t._M_insert_unique(__x); }

map的insert操作调用的也是Rb-tree的_M_insert_unique方法,在这个方法中 最终调用的是_M_get_insert_unique_pos实现了:插入新值,并判断是否重复,若重复插入无效。

 template<typename _Key, typename _Val, typename _KeyOfValue,
           typename _Compare, typename _Alloc>
    pair<typename _Rb_tree<_Key, _Val, _KeyOfValue,
			   _Compare, _Alloc>::_Base_ptr,
	 typename _Rb_tree<_Key, _Val, _KeyOfValue,
			   _Compare, _Alloc>::_Base_ptr>
    _Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::
    _M_get_insert_unique_pos(const key_type& __k)
    {
      typedef pair<_Base_ptr, _Base_ptr> _Res;
      _Link_type __x = _M_begin();
      _Link_type __y = _M_end();
      bool __comp = true;
      while (__x != 0)//从根节点开始,寻找合适的插入点
	{
	  __y = __x;
	  __comp = _M_impl._M_key_compare(__k, _S_key(__x));
	  __x = __comp ? _S_left(__x) : _S_right(__x);
	}
      iterator __j = iterator(__y);//指向插入点的父结点
      if (__comp)//comp为true,说明插在左侧
	{
	  if (__j == begin())//插入结点的父结点为最左侧结点
	    return _Res(__x, __y);
	  else
	    --__j;
	}
//新键值不与既有结点重复,于是执行安插
      if (_M_impl._M_key_compare(_S_key(__j._M_node), __k))
	return _Res(__x, __y);
      return _Res(__j._M_node, 0);
    }

map的[]操作符

map的【】操作符,是根据键值key和实值产生一个临时对象,在调用insert函数将这个临时对象插入到map中去,根据上述insert的描述,我们可以根据返回值的迭代器找到当前的key值对应的元素,由于返回值是引用,可对其元素进行操作。

      mapped_type&
      operator[](const key_type& __k)
      {
	// concept requirements
	__glibcxx_function_requires(_DefaultConstructibleConcept<mapped_type>)

	iterator __i = lower_bound(__k);
	// __i->first is greater than or equivalent to __k.
	if (__i == end() || key_comp()(__k, (*__i).first))
#if __cplusplus >= 201103L
	  __i = _M_t._M_emplace_hint_unique(__i, std::piecewise_construct,
					    std::tuple<const key_type&>(__k),
					    std::tuple<>());
#else
          __i = insert(__i, value_type(__k, mapped_type()));
#endif
	return (*__i).second;
      }

multimap

multimap的特性以及用法和map完全相同,区别在于:它允许键值重复,因此它的插入调用的是rb-tree的_M_insert_equal;

multimap并没有实现【】操作符。

      iterator
      insert(const value_type& __x)
      { return _M_t._M_insert_equal(__x); }

调用的是rb-tree内部的方法,实现如下:

  template<typename _Key, typename _Val, typename _KeyOfValue,
           typename _Compare, typename _Alloc>
#if __cplusplus >= 201103L
    template<typename _Arg>
#endif
    typename _Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::iterator
    _Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::
#if __cplusplus >= 201103L
    _M_insert_equal(_Arg&& __v)
#else
    _M_insert_equal(const _Val& __v)
#endif
    {
      pair<_Base_ptr, _Base_ptr> __res
	= _M_get_insert_equal_pos(_KeyOfValue()(__v));
      return _M_insert_(__res.first, __res.second, _GLIBCXX_FORWARD(_Arg, __v));
    }

上述代码中调用了_M_get_insert_equal_pos,其中的实现是从根节点开始,往下寻找适当的插入点,区别于上述的新值的判断,这里是不做判断是否有重复的。

  template<typename _Key, typename _Val, typename _KeyOfValue,
           typename _Compare, typename _Alloc>
    pair<typename _Rb_tree<_Key, _Val, _KeyOfValue,
			   _Compare, _Alloc>::_Base_ptr,
	 typename _Rb_tree<_Key, _Val, _KeyOfValue,
			   _Compare, _Alloc>::_Base_ptr>
    _Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::
    _M_get_insert_equal_pos(const key_type& __k)
    {
      typedef pair<_Base_ptr, _Base_ptr> _Res;
      _Link_type __x = _M_begin();
      _Link_type __y = _M_end();
      while (__x != 0)//寻找合适的插入点
	{
	  __y = __x;
	  __x = _M_impl._M_key_compare(__k, _S_key(__x)) ?
	        _S_left(__x) : _S_right(__x);//遇大往左,遇小往右
	}
      return _Res(__x, __y);//x为插入点,y为插入点的父结点
    }

 

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值