Allocator(空间配置器)
前言
这本书去年就有看过一点,但是那个时候读源码读的太痛苦,遂放弃了一段时间,后来断断续续又看了点,但是每次碰到allocator部分的知识依旧是云里雾里。现在决定从头开始再看一遍。
以下内容是我阅读 侯捷《STL源码剖析》 这本书的笔记。如果有理解有误的地方还望各位大佬能够不吝指出。
在我们正式开始介绍Allcoator(空间配置器)之前先想一个问题。我们为什么要有空间配置器?这个问题我知道很简单,顾名思义,当然是为了分配空间啦。那分配空间的目的能?那当然是为了存储数据啦。就像书中所言” 整个STL的操作对象(所有数值)都存放在容器之内,而容器一定需要配置空间以存放资料 “
这里将allocator定义为空间配置器而不是内存配置器的原因:因为空间不一定是内存,空间也可以是磁盘或其它辅助存储介质。【书中原话】
学习大纲
SGI 空间配置器的种类及区别
SGI的空间配置器有两种:
- SGI标准的空间配置器,std::allocator
- SGI特殊的空间配置器,std::alloc
它们两者之间的区别:
- 它们的底层实现不同。std::allocator中的底层是用operator new和operator delete实现的空间配置和释放;而std::alloc中使用的则是malloc和free完成的配置和释放;
- std::allocator中,内存配置和对象构造封装在allocate函数中一步完成的(一会介绍原因),内存释放和对象析构也是封装在deallocate函数中一步完成的。而std::alloc中,内存的配置与释放和对象的构造与析构都是使用单独的函数实现的。
::operator new和::operator delete的操作步骤
我们平时调用的new/delete实际上就是::operator new和::operator delete这两个函数。
我们来看下面的语句:
A *a = new A; // 分配内存,然后构造对象
delete a; // 将对象析构,然后释放内存
这个语句等号右边的部分(new A)中实际上进行了两个操作:
- 调用::operator new分配内存;
- 调用A::A()构造对象。
同理,对于delete a;而言也包含两个操作
- 调用A::~A()将对象析构;
- 调用::operator delete释放内存。
这也就为什么我们说在std::allocator中,内存分配和对象构造是被封装在同一个函数中的原因了。因为它的allocate函数的底层实现就是通过operator new来完成的,人家operator new本身就包含了内存分配和对象构造啦,你总不能把人家强行扯开吧~
下面分别介绍std::allocator和std::alloc。
SGI标准的空间配置器,std::allocator
allocate()
template<class T>
inline T* allocate(ptrdiff_t, size. T*){
set_new_handler(0); // 处理内存不足,这里被设置为0,表示遇到内存不足情况的时候直接抛出bad_alloc异常
T* tmp = (T*) (::operator new((size_t)(size * sizeof(T)))); //直接调用new分配内存,并且构造对象
// 当内存分配失败时会打印out of memory,并且强制退出
if (tmp == 0){
cerr << "out of memory" << endl;
exit(1);
}
return tmp;
}
deallocate()
template<class T>
inline void deallocate(T* buffer){
::opertator delete(buffer); // 直接调用delete析构对象,并释放内存
}
SGI特殊的空间配置器,std::alloc
该配置器中对象的构造与析构放在头文件:#incldue<stl_construct.h>
该配置器中空间的配置与释放放在头文件:#incldue<stl_alloc.h>
该配置器的空间分配又分为两种:
- 一级空间配置器
- 二级空间配置器
对象的构造和析构(待补充)
construct()
需要用到头文件 #include<new.h>
中的placement new
#includde <new.h>
// 构造对象
template<class T1, class T2>
inline void construct(T1 *p, T2& value){
new(p) T1(value); // 调用T1::T1()构造函数,并将初始值设定到指针所指的空间上,这个由placement new完成
}
destroy()
destroy()实现析构对象。包括两个版本:
- 仅接受一个指针,也就是仅析构一个对象;
- 接受两个迭代器,析构这两个迭代器之间的值,一般是[first, last),左闭右开。
// 第一个版本的destroy()
template <class T>
inline void destroy(T* pointer){
pointer->~T();
}
// 第二个版本的destory()
template < class ForwardIterator>
inline void destroy(ForwardIterator first, ForwardIterator last){
// 两个参数,[first, last}范围内的对象析构掉
__destroy(first, last, value_type(first)); // 最后一个参数主要是为了判断该迭代器的类型
}
// 判断元素的数值类型是否由trivial destructor
template < class ForwardIterator, class T?
inline void __destroy(ForwardIterator first, ForwardIterator last, T*){
typedef typename __type_traits<T>::