C++ 的两个参考文档
非官方文档:cplusplus
官方文档(同步更新):cppreference
1. 分析:源码及框架
封装红黑树的难度在于结构而非逻辑。
1.1 见一见源码
SGI-STL30 版本源代码中,map 和 set 的源代码在 map / set / stl_map.h / stl_set.h / stl_tree.h 等头文件中。建议查看中间版本的源码,避免最新源码过于复杂的优化干扰理解。
1.2 对比 set 和 map 的源码:泛型编程的应用
底层均使用红黑树实现。区别在于第二个模板参数:value 对于 set 是 key,对 map 则是 pair<const key, T>。
源码中的 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 / erase 的是 Key 对象。
2. map 和 set 的模拟实现
2.1 实现出复用红黑树的框架(支持 insert)
参考源码框架,map 和 set 复用之前实现的红黑树。
调整如下:key 参数用 K,value 参数用 V,红黑树中的数据类型使用 T。
因为 RBTree 实现了泛型不知道 T 参数导致是 K,还是 pair<K, V>,那么 insert 内部进行插入逻辑比较时,就没办法进行比较,因为 pair 的默认支持的是 key 和 value 一起参与比较,我们需要的是任何时候只比较 key。所以在 map 和 set 层分别实现一个 MapKeyOfT 和 SetKeyOfT 的仿函数传给 RBTree 的 KeyOfT,然后 RBTree 中通过 KeyOfT 仿函数取出 T 类型对象中的 key,再进行比较。


