跳到主要内容C++ STL set 与 map 底层封装原理详解 | 极客日志C++算法
C++ STL set 与 map 底层封装原理详解
综述由AI生成本文详细解析了 C++ STL 中 set 和 map 的底层实现机制。通过泛型红黑树 rb_tree 作为基础,利用 KeyOfT 仿函数区分存储类型与比较键值。重点阐述了迭代器的设计,包括 const 与非 const 版本的实现细节,以及 set 中 key 不可变与 map 中 key 不可变 value 可变的约束处理。最后展示了 map 下标运算符重载及插入操作的完整封装逻辑。
云间运维1 浏览 C++ STL set 与 map 底层封装原理详解
我们知道,STL 中的 set 和 map 底层均基于红黑树实现。这一节我们来深入剖析它们是如何在红黑树基础上进行封装的。
STL 容器底层结构
通过查阅资料可以看到,set 通常只传递一个类型参数(Key),而 map 传递两个(Key-Value)。但实际上,库中实现的 set 和 map 底层都是 Key-Value 类型的红黑树。
具体来说:
set<Key> 实际是通过红黑树 rb_tree<Key, Key> 封装实现的;
map<Key, Value> 则是通过红黑树 rb_tree<Key, pair<Key, Value>> 封装实现的。

这意味着两者使用的是同一种树结构,区别仅在于传递给红黑树的第二个模板参数不同:set 是 <Key, Key>,而 map 是 <Key, pair<Key, Value>>。
红黑树的泛型改造
由于 set 存储的是 Key,而 map 存储的是 pair<Key, Value>,我们需要将红黑树节点的模板设计为泛型存储。
节点定义
enum Colour { RED, BLACK };
template<class T>
struct RBTreeNode {
RBTreeNode<T>* _left;
RBTreeNode<T>* _right;
RBTreeNode<T>* _parent;
Colour _col;
T _data;
RBTreeNode(const T& data) :_left(nullptr), _right(nullptr), _parent(nullptr), _data(data), _col(RED) {}
};
此时红黑树类的模板参数虽然定义为两个,但为了适配后续需求,我们还需要增加第三个参数。
插入操作与 KeyOfT
在树的插入操作中,比较的关键是 Key 值。对于 set,第二个模板参数本身就是 Key;对于 map,第二个参数是 pair,需要取出 first 作为 Key。解决这个问题,可以在 set 和 map 层传递第三个模板参数给红黑树,这是一个仿函数类,重载了 () 运算符。
在 set 中实现内部类获取 Key:
MyCreate {
< > {
{
{ key; }
};
:
RBTree<K, K, SetKeyOfT> ;
};
}
#pragma once
#include"RBTree.h"
namespace
template
class
K
class
set
struct
SetKeyOfT
const K& operator()(const K& key)
return
private
_t
在 map 中,树中的 T 是 pair,我们需要取出 pair 中的 first 作为 Key:
#pragma once
#include"RBTree.h"
namespace MyCreate {
template<class K, class V> class map {
struct MapKeyOfT {
const K& operator()(const pair<K, V>& kv) { return kv.first; }
};
private:
RBTree<K, pair<K,V>, MapKeyOfT> _t;
};
}
最后修改红黑树的插入操作,利用第三个模板参数来获取 Key 进行比较。当插入红节点时,需要判断并调整平衡性。
迭代器设计
之前我们跳过了迭代器的实现,现在补上这部分。红黑树迭代器需要支持普通迭代器和 const 迭代器,因此需要三个模板参数。
迭代器定义
template<class T, class Ref, class Ptr>
struct __TreeIterator {
typedef RBTreeNode<T> Node;
Node* _node;
typedef __TreeIterator<T, Ref, Ptr> Self;
__TreeIterator(Node* node) :_node(node) {}
__TreeIterator(const Self& it) :_node(it._node) {}
};
运算符重载
取值与箭头: 使用第二个模板参数 Ref 控制返回值引用类型,第三个参数 Ptr 控制指针类型。
Ref operator*() { return _node->_data; }
Ptr operator->() { return &_node->_data; }
bool operator!=(const Self& s) const { return _node != s._node; }
bool operator==(const Self& s) const { return _node == s._node; }
前置 ++ 逻辑:如果右孩子不为空,访问右子树的最左节点;否则向上寻找第一个作为父亲左孩子的节点。
Self& operator++() {
if (_node->_right) {
Node* cur = _node->_right;
while (cur->_left) cur = cur->_left;
_node = cur;
} else {
Node* cur = _node;
Node* parent = cur->_parent;
while (parent && cur == parent->_right) {
cur = parent;
parent = cur->_parent;
}
_node = parent;
}
return *this;
}
普通与 Const 迭代器
template<class K, class T, class KeyOfT>
class RBTree {
typedef __TreeIterator<T, T&, T*> iterator;
typedef __TreeIterator<T, const T&, const T*> const_iterator;
iterator begin() {
Node* cur = _root;
while (cur->_left) cur = cur->_left;
return iterator(cur);
}
iterator end() { return iterator(nullptr); }
const_iterator begin() const {
Node* cur = _root;
while (cur->_left) cur = cur->_left;
return const_iterator(cur);
}
const_iterator end() const { return const_iterator(nullptr); }
};
容器封装实现
Set 迭代器
set 的 Key 不可修改,本质上只能使用 const 迭代器。在 STL 库中,通常将 const 迭代器也作为普通迭代器使用。
#pragma once
#include"RBTree.h"
namespace MyCreate {
template<class K> class set {
struct SetKeyOfT {
const K& operator()(const K& key) { return key; }
};
public:
typedef typename RBTree<K, K, SetKeyOfT>::const_iterator iterator;
typedef typename RBTree<K, K, SetKeyOfT>::const_iterator const_iterator;
iterator begin() const { return _t.begin(); }
iterator end() const { return _t.end(); }
pair<iterator, bool> insert(const K& key) {
pair<typename RBTree<K, K, SetKeyOfT>::iterator, bool> ret = _t.Insert(key);
return pair<iterator, bool>(ret.first, ret.second);
}
private:
RBTree<K, K, SetKeyOfT> _t;
};
}
注意这里 insert 返回值的处理。因为 set 层的 iterator 是 const_iterator,而红黑树 Insert 返回的是普通迭代器,所以需要显式转换构造。
Map 迭代器
map 的 Key 不可变,Value 可变。我们在红黑树层存储 pair<const K, V>,这样 Key 被锁定为 const,Value 保持可变。
#pragma once
#include"RBTree.h"
namespace MyCreate {
template<class K, class V> class map {
struct MapKeyOfT {
const K& operator()(const pair<K, V>& kv) { return kv.first; }
};
public:
typedef typename RBTree<K, pair<const K, V>, MapKeyOfT>::iterator iterator;
typedef typename RBTree<K, pair<const K, V>, MapKeyOfT>::const_iterator const_iterator;
iterator begin() { return _t.begin(); }
iterator end() { return _t.end(); }
const_iterator begin() const { return _t.begin(); }
const_iterator end() const { return _t.end(); }
pair<iterator, bool> insert(const pair<K, V>& kv) {
return _t.Insert(kv);
}
V& operator[](const K& key) {
pair<iterator, bool> ret = insert(make_pair(key, V()));
return ret.first->second;
}
private:
RBTree<K, pair<const K,V>, MapKeyOfT> _t;
};
}
插入操作封装
set 和 map 的插入操作主要调用红黑树的成员函数。但在 set 中,由于迭代器类型的特殊性,需要像上面那样处理返回值的转换。map 则相对直接,只需确保 Key 的 const 属性正确传递即可。
完整参考代码
RBTree.h
#pragma once
enum Colour { RED, BLACK };
template<class T>
struct RBTreeNode {
RBTreeNode<T>* _left;
RBTreeNode<T>* _right;
RBTreeNode<T>* _parent;
Colour _col;
T _data;
RBTreeNode(const T& data) :_left(nullptr), _right(nullptr), _parent(nullptr), _data(data), _col(RED) {}
};
template<class T, class Ref, class Ptr>
struct __TreeIterator {
typedef RBTreeNode<T> Node;
Node* _node;
typedef __TreeIterator<T, Ref, Ptr> Self;
typedef __TreeIterator<T, T&, T*> Iterator;
__TreeIterator(const Iterator& it) :_node(it._node) {}
__TreeIterator(Node* node) :_node(node) {}
Ref operator*() { return _node->_data; }
Ptr operator->() { return &_node->_data; }
bool operator!=(const Self& s) const { return _node != s._node; }
bool operator==(const Self& s) const { return _node == s._node; }
Self& operator++() {
if (_node->_right) {
Node* cur = _node->_right;
while (cur->_left) cur = cur->_left;
_node = cur;
} else {
Node* cur = _node;
Node* parent = cur->_parent;
while (parent && cur == parent->_right) {
cur = parent;
parent = cur->_parent;
}
_node = parent;
}
return *this;
}
Self& operator--() {
if (_node->_left) {
Node* cur = _node->_left;
while (cur->_right) cur = cur->_right;
_node = cur;
} else {
Node* cur = _node;
Node* parent = cur->_parent;
while (parent && cur == parent->_left) {
cur = parent;
parent = cur->_parent;
}
_node = parent;
}
return *this;
}
};
template<class K, class T, class KeyOfT>
class RBTree {
typedef RBTreeNode<T> Node;
public:
typedef __TreeIterator<T, T&, T*> iterator;
typedef __TreeIterator<T, const T&, const T*> const_iterator;
iterator begin() {
Node* cur = _root;
while (cur->_left) cur = cur->_left;
return iterator(cur);
}
iterator end() { return iterator(nullptr); }
const_iterator begin() const {
Node* cur = _root;
while (cur->_left) cur = cur->_left;
return const_iterator(cur);
}
const_iterator end() const { return const_iterator(nullptr); }
pair<iterator, bool> Insert(const T& data) {
if (_root == nullptr) {
_root = new Node(data);
_root->_col = BLACK;
return make_pair(iterator(_root), true);
}
KeyOfT kot;
Node* cur = _root;
Node* parent = nullptr;
while (cur) {
if (kot(cur->_data) < kot(data)) {
parent = cur;
cur = cur->_right;
} else if (kot(cur->_data) > kot(data)) {
parent = cur;
cur = cur->_left;
} else {
return make_pair(iterator(cur), false);
}
}
cur = new Node(data);
if (kot(parent->_data) < kot(data)) {
parent->_right = cur;
} else {
parent->_left = cur;
}
cur->_parent = parent;
while (parent && parent->_col == RED) {
Node* grandparent = parent->_parent;
if (parent == grandparent->_left) {
Node* uncle = grandparent->_right;
if (uncle && uncle->_col == RED) {
parent->_col = uncle->_col = BLACK;
grandparent->_col = RED;
cur = grandparent;
parent = grandparent->_parent;
} else {
if (cur == parent->_left) {
RotateR(grandparent);
parent->_col = BLACK;
grandparent->_col = RED;
} else {
RotateL(parent);
RotateR(grandparent);
grandparent->_col = RED;
cur->_col = BLACK;
}
break;
}
} else {
Node* uncle = grandparent->_left;
if (uncle && uncle->_col == RED) {
parent->_col = uncle->_col = BLACK;
grandparent->_col = RED;
cur = grandparent;
parent = grandparent->_parent;
} else {
if (cur == parent->_right) {
RotateL(grandparent);
parent->_col = BLACK;
grandparent->_col = RED;
} else {
RotateR(parent);
RotateL(grandparent);
grandparent->_col = RED;
cur->_col = BLACK;
}
break;
}
}
}
_root->_col = BLACK;
return make_pair(iterator(cur), true);
}
void RotateL(Node* parent) {
Node* cur = parent->_right;
Node* curleft = cur->_left;
Node* ppnode = parent->_parent;
parent->_right = curleft;
if (curleft != nullptr) curleft->_parent = parent;
cur->_left = parent;
parent->_parent = cur;
if (ppnode == nullptr) {
_root = cur;
cur->_parent = nullptr;
} else {
if (ppnode->_left == parent) ppnode->_left = cur;
else ppnode->_right = cur;
cur->_parent = ppnode;
}
}
void RotateR(Node* parent) {
Node* cur = parent->_left;
Node* curright = cur->_right;
Node* ppnode = parent->_parent;
parent->_left = curright;
if (curright != nullptr) curright->_parent = parent;
cur->_right = parent;
parent->_parent = cur;
if (ppnode == nullptr) {
_root = cur;
cur->_parent = nullptr;
} else {
if (ppnode->_left == parent) ppnode->_left = cur;
else ppnode->_right = cur;
cur->_parent = ppnode;
}
}
private:
Node* _root = nullptr;
};
MySet.h
#pragma once
#include"RBTree.h"
namespace MyCreate {
template<class K> class set {
struct SetKeyOfT {
const K& operator()(const K& key) { return key; }
};
public:
typedef typename RBTree<K, K, SetKeyOfT>::const_iterator iterator;
typedef typename RBTree<K, K, SetKeyOfT>::const_iterator const_iterator;
iterator begin() const { return _t.begin(); }
iterator end() const { return _t.end(); }
pair<iterator, bool> insert(const K& key) {
pair<typename RBTree<K, K, SetKeyOfT>::iterator, bool> ret = _t.Insert(key);
return pair<iterator, bool>(ret.first, ret.second);
}
private:
RBTree<K, K, SetKeyOfT> _t;
};
}
MyMap.h
#pragma once
#include"RBTree.h"
namespace MyCreate {
template<class K, class V> class map {
struct MapKeyOfT {
const K& operator()(const pair<K, V>& kv) { return kv.first; }
};
public:
typedef typename RBTree<K, pair<const K, V>, MapKeyOfT>::iterator iterator;
typedef typename RBTree<K, pair<const K, V>, MapKeyOfT>::const_iterator const_iterator;
iterator begin() { return _t.begin(); }
iterator end() { return _t.end(); }
const_iterator begin() const { return _t.begin(); }
const_iterator end() const { return _t.end(); }
pair<iterator, bool> insert(const pair<K, V>& kv) {
return _t.Insert(kv);
}
V& operator[](const K& key) {
pair<iterator, bool> ret = insert(make_pair(key, V()));
return ret.first->second;
}
private:
RBTree<K, pair<const K,V>, MapKeyOfT> _t;
};
}
相关免费在线工具
- 加密/解密文本
使用加密算法(如AES、TripleDES、Rabbit或RC4)加密和解密文本明文。 在线工具,加密/解密文本在线工具,online
- Gemini 图片去水印
基于开源反向 Alpha 混合算法去除 Gemini/Nano Banana 图片水印,支持批量处理与下载。 在线工具,Gemini 图片去水印在线工具,online
- Base64 字符串编码/解码
将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online
- Base64 文件转换器
将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online
- Markdown转HTML
将 Markdown(GFM)转为 HTML 片段,浏览器内 marked 解析;与 HTML转Markdown 互为补充。 在线工具,Markdown转HTML在线工具,online
- HTML转Markdown
将 HTML 片段转为 GitHub Flavored Markdown,支持标题、列表、链接、代码块与表格等;浏览器内处理,可链接预填。 在线工具,HTML转Markdown在线工具,online