C++ 基于红黑树封装 map 与 set 详解
在完成了红黑树的基础实现后,接下来需要处理一些关键细节,主要是 const 迭代器的控制以及 map 特有的 operator[] 实现。这部分内容往往容易踩坑,尤其是涉及模板参数和类型转换时。
一、红黑树封装中的 const 迭代器
1. 核心思路
要实现 const 迭代器,关键在于通过模板参数控制 operator* 和 operator-> 的返回值类型。普通迭代器返回引用或指针,允许修改;const 迭代器则返回 const 引用或指针,禁止修改。
我们需要在迭代器结构体中定义三个模板参数:节点类型、指针类型、引用类型。通过传入不同的类型实例化,就能区分普通迭代器和 const 迭代器。
2. Set 的实现
Set 容器存储的是 key,且 key 本身不可变。因此,无论是普通迭代器还是 const 迭代器,其底层都应该是红黑树的 const 迭代器。这样无论怎么访问,都无法修改 key 的值,符合 Set 的设计语义。
在代码层面,我们将 iterator typedef 为红黑树的 const_iterator。注意这里需要使用 typename 关键字,因为它是依赖类型。
3. Map 的特殊处理
Map 存储的是 pair<key, value>。与 Set 不同,Map 的 key 不可变,但 value 是可变的。如果直接将 Map 的普通迭代器也设为 const 迭代器,那么 value 也无法修改了,这不符合需求。
解决方案是在 Map 内部将存储类型定义为 pair<const K, V>。这样 key 被强制为 const,而 value 保持可变。迭代器逻辑可以复用红黑树的普通迭代器,自然就能满足 key 只读、value 可写的要求。
二、operator[] 的实现
Map 需要提供 operator[] 来支持通过下标访问和插入。它的行为逻辑是:
- 如果 key 存在,返回对应的 value 引用。
- 如果 key 不存在,插入一个默认构造的 value,并返回该 value 引用。
这个功能必须借助 insert 方法来实现。为了配合 operator[],insert 方法的返回值最好是一个 pair<iterator, bool>,其中 iterator 指向新插入或已存在的元素,bool 表示是否插入成功。
1. Insert 的返回值调整
之前的 insert 可能只返回 void 或者简单的状态,现在需要调整为返回 pair。同时,由于 Map 和 Set 对迭代器的定义不同,insert 在 Set 中可能会遇到编译问题。
2. Set 中的编译问题
当我们在 Set 中调用 insert 并尝试获取返回的 iterator 时,会发现编译报错。这是因为 Set 的 iterator 别名实际上是 const_iterator,而 insert 返回的是红黑树内部的普通迭代器(用于内部操作)。
解决思路是接收红黑树内部的迭代器,然后将其转换为 Set 定义的 iterator(即 const_iterator)。这需要提供一个构造函数,允许用普通迭代器初始化 const 迭代器。
// Set 中的 insert 实现
pair<iterator, bool> insert(const K& key) {
// 先调用底层红黑树的 insert
pair< RBTree<K, K, SetKeyOfT>::iterator, > ret = .(key);
<iterator, >(ret.first, ret.second);
}


