【C++】详解STL之set和map --- 关联式容器的再封装

目录

1.源码及框架分析

2.模拟实现出set和map

(1) 实现出复用红黑树的框架,并支持insert

(2) 支持迭代器的实现

① iterator核心源码(stl_tree.h)

② iterator实现思路分析

③ iterator模拟实现

④ rbtree迭代器版模拟实现

⑤ map要支持operator[]

 ⑥myset和mymap模拟实现


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(容易理
评论 20
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值