手撕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);
}