目录
1.源码及框架分析
SGI-STL30版本源代码,
map和set
的源代码在map / set / stl_map.h / stl_set.h / stl_tree.h等几个头文件中。
// set
#ifndef __SGI_STL_INTERNAL_TREE_H
#include <stl_tree.h>
#endif
#include <stl_set.h>
#include <stl_multiset.h>
// map
#ifndef __SGI_STL_INTERNAL_TREE_H
#include <stl_tree.h>
#endif
#include <stl_map.h>
#include <stl_multimap.h>
map和set的实现结构框架核心部分截取出来如下:
// stl_set.h
template <class Key, class Compare = less<Key>, class Alloc = alloc>
class set {
public:
// typedefs:
typedef Key key_type;
// 不是日常意义上的value,而是红黑树结点中存储的真实的数据的类型(key)。
typedef Key value_type;
private:
typedef rb_tree<key_type, value_type,
identity<value_type>, key_compare, Alloc> rep_type;
// key_compare 是仿函数,为了兼容map的仿函数比较,做出的牺牲
rep_type t; // red-black tree representing set
};
// stl_map.h
template <class Key, class T, class Compare = less<Key>, class Alloc = alloc>
class map {
public:
// typedefs:
typedef Key key_type;
typedef T mapped_type;
// pair的Key写成const,避免Key值被修改
// 不是日常意义上的value,而是红黑树结点中存储的真实的数据的类型(pair)
typedef pair<const Key, T> value_type;
private:
typedef rb_tree<key_type, value_type,
select1st<value_type>, key_compare, Alloc> rep_type;
// 仿函数key_compare,实现pair的first比较
// (因为pair直接比较会兼容second的比较,不是我们想要的)
rep_type t; // red-black tree representing map
};
// stl_tree.h
struct __rb_tree_node_base // 红黑树基础节点
{
typedef __rb_tree_color_type color_type;
typedef __rb_tree_node_base* base_ptr;
color_type color;
base_ptr parent;
base_ptr left;
base_ptr right;
};
// 一份模版, 编译器根据所传过来的Value,
// 实例化出value是key / pair的RB tree
template <class Value>
struct __rb_tree_node : public __rb_tree_node_base
{
typedef __rb_tree_node<Value>* link_type;
Value value_field; // 节点值
};
// stl_tree.h
template <class Key, class Value, class KeyOfValue, class Compare, class Alloc
= alloc>
class rb_tree {
protected:
typedef void* void_pointer;
typedef __rb_tree_node_base* base_ptr;
typedef __rb_tree_node<Value> rb_tree_node;
typedef rb_tree_node* link_type;
typedef Key key_type;
typedef Value value_type; // 传给Value的数据才是要存储到红黑树节点的数据
public:
// insert用的是第二个模板参数左形参
pair<iterator, bool> insert_unique(const value_type& x);
// erase和find用第一个模板(Key)参数做形参
// 注:这里是泛型编程(template<>),所以得定义Key,所以RB tree模版仍要传Key
size_type erase(const key_type& x);
iterator find(const key_type& x);
protected:
size_type node_count; // keeps track of size of tree
link_type header; // 哨兵位节点
};
• 通过下图对框架的分析,我们可以看到源码中rb_tree用一个巧妙的泛型思想实现,rb_tree是实 现key的搜索场景,还是key/value的搜索场景不是直接写死的,而是由第二个模板参数Value决定_rb_tree_node中存储的数据类型。• set实例化rb_tree时第而个模板参数给的是key,map实例化rb_tree时第二个模板参数给的是pair<const key, T> ,这样一颗红黑树既可以实现key搜索场景的set,也可以实key/value搜索场 景的map。• 要注意一下,源码里面模板参数是用T代表value,而内部写的value_type不是我们我们日常 key/value场景中说的value,源码中的 value_type 反而是红黑树结点中存储的真实的数据的类型。• rb_tree第二个模板参数Value已经控制了红黑树结点中存储的数据类型,为什么还要传第一个模板 参数Key呢?尤其是set,两个模板参数是一样的,这是很多同学这时的一个疑问。要注意的是对于 map和set,find/erase时的函数参数都是Key,所以第一个模板参数是传给find/erase等函数做形参的类型的。对于set而言两个参数是一样的,但是对于map而言就完全不一样了,map insert的是pair对象,但是find和ease的是Key对象。• 这里源码命名风格比较乱,set模板参数用的Key命名,map用的是Key和T命名,而 rb_tree用的又是Key和Value。
2.模拟实现出set和map
(1) 实现出复用红黑树的框架,并支持insert
• 参考源码框架,map和set复用之前我们实现的红黑树。• 我们这里相比源码调整一下,key参数就用K,value参数就用V,红黑树中的数据类型,我们使用 T(容易理