手撕MyTinySTL项目
项目简介
复刻基本思路
前期准备
1.type_traits.h
classDiagram
%% 编译期常量包装器
class m_integral_constant~T, v~ {
<<template>>
+value: T
}
%% 布尔常量类型
class m_bool_constant~b~ {
<<type alias>>
}
m_bool_constant~b~ --|> m_integral_constant~bool, b~
%% true/false 类型
class m_true_type {
<<type alias>>
}
class m_false_type {
<<type alias>>
}
m_true_type --|> m_bool_constant~true~
m_false_type --|> m_bool_constant~false~
%% is_pair 类型萃取
class is_pair~T~ {
<<template>>
+继承: m_false_type
}
class is_pair~pair~ {
<<template特化>>
+继承: m_true_type
}
%% pair 前向声明
class pair~T1, T2~ {
<<forward declaration>>
}
%% 关系
is_pair~pair~ --|> is_pair~T~
is_pair~T~ --|> m_false_type
is_pair~pair~ --|> m_true_type
说明:
- m_integral_constant 是基础的编译期常量包装器模板。
- m_bool_constant、m_true_type、m_false_type 都是类型别名,分别继承自 m_integral_constant 或 m_bool_constant。
- is_pair
默认继承自 m_false_type,特化版本 is_pair<pair<T1, T2» 继承自 m_true_type,用于类型萃取判断。 - pair<T1, T2> 是前向声明,表示 mystl 命名空间下的 pair 模板。
2.util.h
classDiagram
%% mystl命名空间下的工具函数
class mystl {
<<namespace>>
+move(T)
+forward(T)
+swap(lhs, rhs)
+swap_range(first1, last1, first2)
+make_pair(first, second)
}
%% pair模板结构体
class pair~Ty1, Ty2~ {
+first: Ty1
+second: Ty2
+pair()
+pair(const Ty1&, const Ty2&)
+pair(const pair&)
+pair(pair&&)
+swap(pair& other)
+operator=()
+~pair()
...
}
%% 关联关系
mystl ..> pair : 使用/生成
mystl <|.. pair : 工具函数支持
说明:
- mystl 表示命名空间,包含所有工具函数(move、forward、swap、swap_range、make_pair)。
- pair 是模板结构体,拥有 first、second 两个成员和多种构造、赋值、交换等方法。
- 箭头表示 mystl 工具函数与 pair 的关系(如 make_pair 生成 pair,swap 支持 pair)。
- 省略号 … 表示 pair 还有其他重载构造和赋值函数,具体可查源码。
3.iterator.h
这份代码主要是用于说明和实现 C++ STL 迭代器体系结构,包括迭代器类型标签、traits 萃取、辅助工具函数、反向迭代器模板等。它为自定义容器和算法提供了统一的迭代器接口和类型信息,是泛型编程的基础。
classDiagram
%% 迭代器类型标签继承体系
class input_iterator_tag
class output_iterator_tag
class forward_iterator_tag
class bidirectional_iterator_tag
class random_access_iterator_tag
forward_iterator_tag --|> input_iterator_tag
bidirectional_iterator_tag --|> forward_iterator_tag
random_access_iterator_tag --|> bidirectional_iterator_tag
%% 迭代器模板
class iterator~Category, T, Distance, Pointer, Reference~ {
+iterator_category
+value_type
+pointer
+reference
+difference_type
}
%% 迭代器traits
class iterator_traits~Iterator~ {
+iterator_category
+value_type
+pointer
+reference
+difference_type
}
%% 反向迭代器
class reverse_iterator~Iterator~ {
-current: Iterator
+base()
+operator*()
+operator->()
+operator++()
+operator--()
+operator+=()
+operator-=()
+operator[]()
}
%% 关系
reverse_iterator~Iterator~ o-- iterator_traits~Iterator~ : 使用
iterator_traits~Iterator~ ..> iterator~Category, T, Distance, Pointer, Reference~ : 依赖
说明:
- 左侧为五种迭代器类型标签,展示了它们的继承体系(如 random_access_iterator_tag 继承自 bidirectional_iterator_tag)。
- iterator 模板类定义了标准迭代器的五种型别(类型别名)。
- iterator_traits 模板类用于萃取任意迭代器的型别信息,是泛型算法的基础。
- reverse_iterator 模板类实现了反向迭代器,支持常用的运算符重载。
- 箭头和关联展示了继承、依赖和使用关系。
4.algobase.h
这份代码主要是用于说明和实现 STL 常用基础算法,如 max、min、copy、move、fill、lexicographical_compare、mismatch 等。它为自定义容器和泛型算法提供了高效、类型安全的基础操作,是 MyTinySTL 算法层的核心组成部分。
classDiagram
%% mystl 命名空间下的算法函数
class mystl {
<<namespace>>
+max(lhs, rhs)
+min(lhs, rhs)
+iter_swap(lhs, rhs)
+copy(first, last, result)
+copy_backward(first, last, result)
+copy_if(first, last, result, unary_pred)
+copy_n(first, n, result)
+move(first, last, result)
+move_backward(first, last, result)
+equal(first1, last1, first2)
+fill(first, last, value)
+fill_n(first, n, value)
+lexicographical_compare(first1, last1, first2, last2)
+mismatch(first1, last1, first2)
}
%% 依赖的类型
class pair~T1, T2~ {
+first: T1
+second: T2
}
%% 算法与类型的关系
mystl ..> pair~T1, T2~ : 返回/使用
说明:
- mystl 表示命名空间,包含所有基础算法函数(max、min、copy、move、fill、equal、lexicographical_compare、mismatch 等)。
- pair 是 mystl 算法常用的返回类型(如 copy_n、mismatch 返回 pair)。
- 箭头表示 mystl 算法与 pair 类型的依赖关系。
5.construct.h
这份代码主要是用于说明和实现对象的构造与析构机制,即 STL 容器底层常用的 construct/destroy 工具。它通过 placement new 实现原地构造对象,通过类型萃取和标签分发机制实现高效、安全的对象析构。这样设计可以让容器灵活管理对象生命周期,提升效率并避免资源泄漏。
classDiagram
%% mystl 命名空间下的构造与析构工具
class mystl {
<<namespace>>
+construct(ptr)
+construct(ptr, value)
+construct(ptr, Args&&...)
+destroy_one(ptr, std::true_type)
+destroy_one(ptr, std::false_type)
+destroy_cat(first, last, std::true_type)
+destroy_cat(first, last, std::false_type)
+destroy(ptr)
+destroy(first, last)
}
%% 类型特征依赖
class is_trivially_destructible~T~ {
<<type trait>>
+value: bool
}
%% 关系
mystl ..> is_trivially_destructible~T~ : 类型分发
说明:
- mystl 表示命名空间,包含所有对象构造与析构相关的函数模板(construct、destroy_one、destroy_cat、destroy)。
- std::is_trivially_destructible 是类型特征(type trait),用于在 destroy 分发时判断类型是否需要显式析构。
- 箭头表示 mystl 的 destroy 系列函数依赖类型特征进行标签分发(即根据类型是否平凡析构选择不同的析构策略)。
destroy析构方法的执行流程:
sequenceDiagram
participant UserCode as 用户代码
participant destroy_single as destroy(Ty* pointer)
participant destroy_range as destroy(ForwardIter first, last)
participant destroy_one as destroy_one(Ty*, ...)
participant destroy_cat as destroy_cat(ForwardIter, ...)
participant is_trivial as std::is_trivially_destructible
Note over UserCode: 析构单个对象
UserCode ->> destroy_single: 调用destroy(pointer)
destroy_single ->> is_trivial: 检查Ty是否平凡析构
alt Ty是平凡类型
is_trivial -->> destroy_one: std::true_type
destroy_one ->> destroy_one: 空操作(模板特化)
else Ty是非平凡类型
is_trivial -->> destroy_one: std::false_type
destroy_one ->> destroy_one: 显式调用pointer->~Ty()
end
Note over UserCode: 析构范围对象
UserCode ->> destroy_range: 调用destroy(first, last)
destroy_range ->> is_trivial: 检查元素类型是否平凡析构
alt 元素是平凡类型
is_trivial -->> destroy_cat: std::true_type
destroy_cat ->> destroy_cat: 空操作(模板特化)
else 元素是非平凡类型
is_trivial -->> destroy_cat: std::false_type
loop 遍历每个元素
destroy_cat ->> destroy_single: 调用destroy(&*first)
Note right of destroy_cat: 解引用迭代器并取地址
destroy_single -->> destroy_cat: 完成单个析构
destroy_cat ->> destroy_cat: ++first
end
end
6.allocator.h
这份代码主要是用于说明和实现 STL 容器底层的内存分配器 allocator。它负责对象的内存分配、释放,以及对象的构造与析构。allocator 是 STL 容器泛型内存管理的基础,所有容器都可以通过 allocator 实现类型无关的内存操作和对象生命周期管理。
classDiagram
class allocator~T~ {
+value_type
+pointer
+const_pointer
+reference
+const_reference
+size_type
+difference_type
+allocate()
+allocate(n)
+deallocate(ptr)
+deallocate(ptr, n)
+construct(ptr)
+construct(ptr, const T&)
+construct(ptr, T&&)
+construct(ptr, Args&&...)
+destroy(ptr)
+destroy(first, last)
}
%% 依赖关系
class ConstructDestroyTools {
<<工具函数>>
+construct(...)
+destroy(...)
}
allocator~T~ ..> ConstructDestroyTools : 构造/析构依赖
说明:
- allocator
是模板类,负责内存分配、释放、对象构造与析构,所有成员均为静态方法,便于泛型调用。 - ConstructDestroyTools 代表 mystl 命名空间下的 construct/destroy 工具函数(实际为 mystl::construct 和 mystl::destroy),allocator 的构造和析构方法都依赖这些工具函数实现对象的生命周期管理。
- 箭头表示 allocator 的 construct/destroy 方法依赖于 mystl 的工具函数。
阶段一:基础容器与数据结构
- 目标:掌握基础容器的实现原理和内存管理。
- 核心头文件:vector.h, list.h, deque.h, stack.h, queue.h
- 学习内容:
(1)查看源代码顺序
type_traits.h—>
顺序容器
(1)vector.h:动态数组实现,重点学习:
- 动态扩容策略(2 倍扩容 vs 固定步长)
- 内存连续性与迭代器失效机制
- push_back/erase 的时间复杂度
(2)list.h:双向链表实现,分析:
- 节点结构(prev/next/data)
- 链表迭代器的实现(如何维护指针)
- splice 操作的高效性
(3)deque.h:双端队列的分段连续内存设计,理解:
- 中控器(map)与缓冲区(buffer)结构
- 随机访问的复杂度优化
容器适配器
stack.h 和 queue.h:基于底层容器(如 deque 或 list)的封装,学习:
- 如何通过组合而非继承实现适配器模式
- 限制接口的设计(如禁止 stack 的随机访问)
实践方法
- 手写实现:从零实现 vector 和 list,模仿 push_back、insert、erase 等接口。
- 对比标准库:用 GDB 调试标准库的 std::vector 和 MyTinySTL 的 vector.h,观察内存布局差异。
阶段二:关联容器与内存管理 目标:深入哈希表、红黑树和内存分配机制。 核心头文件:hashtable.h, rb_table.h, unordered_map.h, unordered_set.h, allocator.h, memory.h 学习内容:
关联容器: hashtable.h:链地址法哈希表实现,重点分析: 哈希函数设计(如取模运算的优化) 桶(bucket)的动态扩容与负载因子控制 冲突解决策略(链表 vs 开放寻址) rb_table.h:红黑树实现,学习: 红黑树的 5 条规则与平衡调整(左旋/右旋) 如何通过红黑树实现 map.h 和 set.h 内存管理: allocator.h:内存分配器设计,重点: 对象构造/析构分离(construct.h/destroy.h) 内存池实现(避免频繁调用 new/delete) memory.h:智能指针(如 shared_ptr)和内存工具(uninitialized_copy)。 实践方法:
实现哈希表:手写一个简化版 hashtable,支持插入、查找和动态扩容。 红黑树调试:在 rb_table.h 中插入节点,通过打印节点颜色和结构验证平衡性。 阶段三:算法、迭代器与模板元编程 目标:掌握算法与容器的协作逻辑及泛型编程技巧。 核心头文件:algo.h, algorithm.h, iterator.h, type_traits.h, functional.h 学习内容:
算法与迭代器: algo.h:基础算法(如 sort、find),分析: 如何通过迭代器泛化容器操作(如 RandomAccessIterator 与 BidirectionalIterator 的区别) 快排、插入排序的混合优化策略 iterator.h:迭代器萃取机制(iterator_traits),理解: 迭代器分类(input_iterator_tag 等) 如何通过 distance 和 advance 实现泛型移动 模板元编程: type_traits.h:类型萃取技术,学习: 判断类型特性(如 is_integral、is_pointer) 实现编译期条件分支(如 enable_if) functional.h:仿函数(greater、less)与绑定器(bind)。 实践方法:
优化算法:在 algo.h 中添加自定义算法(如归并排序),并通过迭代器适配不同容器。 类型萃取实战:利用 type_traits.h 实现一个编译期类型检查工具。 阶段四:高级主题与系统化整合 目标:理解异常处理、并发安全与性能优化。 核心头文件:except.h, util.h, memory.h(线程安全扩展) 学习内容:
异常处理: except.h:学习 MyTinySTL 的异常传播机制(如 try/catch 在容器中的使用)。 并发与性能: 为 hashtable.h 或 rb_table.h 添加线程安全锁(如 mutex)。 分析 vector 扩容时的内存分配性能,尝试实现内存池优化。 实践方法:
线程安全改造:为 unordered_map.h 添加细粒度锁,支持多线程插入/查找。 性能测试:用 chrono 库对比 MyTinySTL 与标准库的 sort 算法性能。 学习路径总结 按模块分阶段:从容器→内存→算法→高级特性逐步推进,避免一次性 overwhelmed。 代码精读+手写:对每个头文件,先阅读源码注释,再手写简化版实现(例如先实现 vector 的 push_back,再逐步完善)。 对比与调试: 通过 g++ -E 生成预处理文件,观察模板展开后的代码。 对比 MyTinySTL 与 libstdc++(GCC 标准库)的同类组件实现差异。 实战驱动: 为 MyTinySTL 添加新功能(如 span 容器或并行算法),提交 Pull Request。 在 LeetCode 或实际项目中尝试替换标准库组件,验证可靠性和性能。 资源推荐 书籍: 《STL 源码剖析》- 侯捷(对照本书分析 MyTinySTL 的设计差异) 《Effective STL》- Scott Meyers(学习高效使用 STL 的实践技巧) 工具: Compiler Explorer:在线查看模板实例化后的汇编代码。 Valgrind:检测 MyTinySTL 的内存泄漏问题。
复刻项目阶段安排
复刻项目三阶段
结论
参考资料
附录1:个人喜好记录
记录自己觉得写得很好得代码,他们就像你读到一段很美得名言警句一样。
(1)从后往前复制数据到新数组中
while (first != last)
*--result = *--last;(1)移动语义
template <class T>
typename std::remove_reference<T>::type&& move(T&& arg) noexcept
{
return static_cast<typename std::remove_reference<T>::type&&>(arg);
}(2)完美转发
template <class T>
T&& forward(typename std::remove_reference<T>::type& arg) noexcept
{
return static_cast<T&&>(arg);
}