C++进阶——STL源码之set与multiset

14 篇文章 4 订阅

STL源码之set与multiset

set

set的特性是,所有元素都会根据元素的键值自动被排序,set元素的键值就是实值,实值就是键值,set不允许两个元素有相同的键值。

set的元素值不可以通过迭代器来改变,因为set元素就是其键值,关系到set元素的排列规则;如果任何改变set元素值,会严重破坏set组织。

set是以rb-tree作为底层机制,又由于set所开放的各种操作接口,rb-tree也提供,所以几乎所有的set操作行为都只是调用rb-tree的操作行为。

set源码

1. set的构成

set的内部成员变量只有一个  _Rep_type _M_t;  // Red-black tree representing set.

set的内部迭代器全部都是const_iterator,杜绝写入操作

set的成员变量类型中  typedef _Rb_tree<key_type, value_type, _Identity<value_type>,key_compare, _Key_alloc_type> _Rep_type可以看出,构造的rb-tree的value和key是同一个类型。

  template<typename _Key, typename _Compare = std::less<_Key>,
	   typename _Alloc = std::allocator<_Key> >
    class set
    {
      // concept requirements
      typedef typename _Alloc::value_type                   _Alloc_value_type;
      __glibcxx_class_requires(_Key, _SGIAssignableConcept)
      __glibcxx_class_requires4(_Compare, bool, _Key, _Key,
				_BinaryFunctionConcept)
      __glibcxx_class_requires2(_Key, _Alloc_value_type, _SameTypeConcept)

    public:
      // typedefs:
      //@{
      /// Public typedefs.
      typedef _Key     key_type;
      typedef _Key     value_type;
      typedef _Compare key_compare;
      typedef _Compare value_compare;
      typedef _Alloc   allocator_type;
      //@}

    private:
      typedef typename __gnu_cxx::__alloc_traits<_Alloc>::template
	rebind<_Key>::other _Key_alloc_type;

      typedef _Rb_tree<key_type, value_type, _Identity<value_type>,
		       key_compare, _Key_alloc_type> _Rep_type;
      _Rep_type _M_t;  // Red-black tree representing set.

      typedef __gnu_cxx::__alloc_traits<_Key_alloc_type> _Alloc_traits;

    public:
      //@{
      ///  Iterator-related typedefs.
      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;
      // _GLIBCXX_RESOLVE_LIB_DEFECTS
      // DR 103. set::iterator is required to be modifiable,
      // but this allows modification of keys.
      typedef typename _Rep_type::const_iterator            iterator;
      typedef typename _Rep_type::const_iterator            const_iterator;
      typedef typename _Rep_type::const_reverse_iterator    reverse_iterator;
      typedef typename _Rep_type::const_reverse_iterator const_reverse_iterator;
      typedef typename _Rep_type::size_type                 size_type;
      typedef typename _Rep_type::difference_type           difference_type;
      //@}

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

。。。
}

2. 元素的插入操作

元素的插入操作insert实现如下,它内部调用的是_M_insert_unique,在C++进阶——STL源码之红黑树(_Rb_tree)一文中,我们已经分析过该函数,它对于新值的插入是不允许结点重复的,若重复,插入无效。

      std::pair<iterator, bool>
      insert(const value_type& __x)
      {
	std::pair<typename _Rep_type::iterator, bool> __p =
	  _M_t._M_insert_unique(__x);
	return std::pair<iterator, bool>(__p.first, __p.second);
      }

 

  template<typename _Key, typename _Val, typename _KeyOfValue,
           typename _Compare, typename _Alloc>
#if __cplusplus >= 201103L
    template<typename _Arg>
#endif
    pair<typename _Rb_tree<_Key, _Val, _KeyOfValue,
			   _Compare, _Alloc>::iterator, bool>
    _Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::
#if __cplusplus >= 201103L
    _M_insert_unique(_Arg&& __v)
#else
    _M_insert_unique(const _Val& __v)
#endif
    {
      typedef pair<iterator, bool> _Res;
      pair<_Base_ptr, _Base_ptr> __res
	= _M_get_insert_unique_pos(_KeyOfValue()(__v));
 
      if (__res.second)
	return _Res(_M_insert_(__res.first, __res.second,
			       _GLIBCXX_FORWARD(_Arg, __v)),
		    true);
 
      return _Res(iterator(static_cast<_Link_type>(__res.first)), false);
    }

_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);
    }

multiset

multiset的特性以及用法和set完全相同,唯一的差别在于它允许键值重复,因此它的插入操作采用的是底层机制rb-tree的_M_insert_equal。

      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为插入点的父结点
    }

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值