STL源码剖析(十四)关联式容器之set、multiset

STL源码剖析(十四)关联式容器之set、multiset

一、set、multiset简介

关联式容器之所以称为关联式,那是应为每个节点都有key和data

key和data组合到一起叫做value

在这里插入图片描述

set是一种比较特殊的关联式容器,应为它没有data,所以key就是value,value就是key

set 和 multiset 的区别就是,set 插入的key不能重复,而 multi_set 可以

二、set、multiset的数据结构

template <class Key, class Compare = less<Key>, class Alloc = alloc>
class set {
public:
  // typedefs:

  typedef Key key_type;
  typedef Key value_type;
  typedef Compare key_compare;
  ...
private:
  typedef rb_tree<key_type, value_type, 
                  identity<value_type>, key_compare, Alloc> rep_type;
  rep_type t;  // red-black tree representing set
  ...
};

set里面只是定义了一棵红黑树,下面来看看它指定红黑树的模板参数

首先看看红黑树的模板参数定义

template <class Key, class Value, class KeyOfValue, class Compare,
          class Alloc = alloc>
class rb_tree {
  ...
};
  • Key表示键值

  • Value指的是key加data组合成的结构,对于set来说,由于没有data,所以它依旧是key

  • KeyOfValue是从value取出key的方法,在set中指定了 identity<value_type>,这个是一个仿函数,其作用就是返回自身,如下定义

    template <class T>
    struct identity : public unary_function<T, T> {
      const T& operator()(const T& x) const { return x; }
    };
    
  • Compare是比较键值的方法,set默认采用的是 less<Key>,其定义如下

    template <class T>
    struct less : public binary_function<T, T, bool> {
        bool operator()(const T& x, const T& y) const { return x < y; }
    };
    

搞清楚红黑树的模板参数的意义,那么set的数据结构也就搞清楚了

multi_set 的定义和 set 的定义其实是一样的,如下

template <class Key, class Compare = less<Key>, class Alloc = alloc>
class multiset {
  ...
private:
  typedef rb_tree<key_type, value_type, 
                  identity<value_type>, key_compare, Alloc> rep_type;
  rep_type t;  // red-black tree representing multiset
  
  ...
};

三、set、multiset的迭代器

set 和 multiset 的迭代器都是一样的,如下定义

typedef typename rep_type::const_iterator iterator;

其实它们并没有自己的迭代器,而是直接使用红黑树的迭代器

另外需要注意的一点是,这里使用的是 const_iterator,常量迭代器,是因为红黑树中的key是不允许被改变的

四、set、multiset的操作

set 和 multiset 的操作主要区别在于插入操作,所以这里只将插入操作有所区别,其他操作都是通用的

4.1 构造函数

默认构造

set() : t(Compare()) {}

初始化其中的红黑树

拷贝构造

set(const set<Key, Compare, Alloc>& x) : t(x.t) {}

拷贝其中的红黑树

4.2 析构函数

set没有定义析构函数,当set内存被释放时,红黑树也会自动被析构释放

4.3 插入元素

set 的 insert

typedef  pair<iterator, bool> pair_iterator_bool; 
pair<iterator,bool> insert(const value_type& x) { 
  pair<typename rep_type::iterator, bool> p = t.insert_unique(x); 
  return pair<iterator, bool>(p.first, p.second);
}

set 的 insert 调用的是红黑树的 insert_unique,它不支持键值重复

返回值是一个对组,包含一个指向插入节点的迭代器还有结果

multiset 的 insert

iterator insert(const value_type& x) { 
  return t.insert_equal(x);
}

multiset 调用的是 insert_equal,它支持键值重复

放回值是指向插入节点的迭代器,因为允许键值重复,所以插入一定成功,因此没有返回插入结果

4.4 删除元素

size_type erase(const key_type& x) { 
  return t.erase(x);
}

调用红黑树的erase

4.5 其他操作

begin

iterator begin() const { return t.begin(); }

end

iterator end() const { return t.end(); }

find

iterator find(const key_type& x) const { return t.find(x); }

可以发现,set 和 multiset 的操作基本上都是通过底层的红黑树实现的

发布了107 篇原创文章 · 获赞 197 · 访问量 8万+

猜你喜欢

转载自blog.csdn.net/weixin_42462202/article/details/102237137