STL 源码分析-A.万物起源-1
C++中,我们熟悉的对象的创建和释放过程如下:
1 | class Thing |
代码中 new 完成两部分动作,1. 调用 ::operator new
分配对象所占内存空间,2. 调用 Thing 构造函数初始化对象内容。delete
也完成两部分动作,1. 调用析构函数清除对象内容,2. 调用
::operator delete
释放内存空间。
STL 将这两部分动作分离了开来,内存分配由
alloc:allocate()
负责,释放由
alloc:deallocate()
负责,对象构造和析构依次由
::contruct()
和 ::destroy()
负责。
具体实现位于 stl_alloc.h
, stl_contruct.h
,
stl_uninitialized.h
中,stl_alloc.h
负责内存分配,stl_contruct.h
负责对象构建和析构,stl_uninitialized.h
提供一系列全局函数来进行内存填充 (fill) 和拷贝 (copy)。其实 alloc
并不符合标准规范,而应该是 allocator,位于 stl_defalloc.h
中。
1 | template <class T> |
首先定义两个全局函数,用来进行内存分配的操作,可以看出其只是对
::operator new
和 ::operator delete
做了一层简单的封装。
1 | template <class T> |
这便是 allocator 的实现,因为效率不好,所以 SGI STL
从来没有使用过它,也并不建议我们使用。SGI STL 实现的 alloc 除了
allocator
的功能外,还考虑内存不足时的处理,分配过多小区域导致内存碎片的问题等。关于
alloc 后面再谈,先从对象构建和析构着手 (stl_contruct.h
)
入手。
1 | template <class _T1, class _T2> |
new (__p) _T1( … )
采用的是 placement new 操作,不同于
::operator new
从堆中直接分配内存用以对象构建,它是在一个已经分配好的内存区域中构建对象,正因为这样,我们才能分离对象构建析构和内存分配释放。
1 | template <class _Tp> |
对象的析构从
destroy(_ForwardIterator __first, _ForwardIterator __last)
(L27) 开始,依次对 foward iterator
所指向的所有对象进行析构。首先将迭代器 (指向对象) 类型传入
__destroy
(L28) 函数,该函数对传入类型进行
type_traits
(L21) (迭代器和 traits 相关内容请阅读),然后跟据该对象有无
trivial destructor
,分别由重载函数 (L15) (L8)
进行处理,为什么这样做呢?
因为我们不知道 L27 中迭代器的区间范围有多大,如果数量很多,调用那些
trivial destructor 在效率方面是一种浪费。如果是 trivial destructor
(L15),则什么都不做,如果不是 (L8),便依次调用
destroy(_Tp* __pointer)
(L2),而该函数则直接调用对象的析构函数,整个析构过程结束。
本文代码示例基于 SGI STL 3.2.
下一篇将分析 alloc 的实现。