本文作者:campos 本文出处:http://www.mykernelspace.com (转载请保留此行,谢谢)
allocator是STL里面的无名英雄,几乎所有的container都要包含它,但是很少有用户直接包含这个文件。事实上所有的容器都需要内存管理,所以它们都包含了包含了 bits/allocator.h 。而这个文件又包含了bits/c++allocator.h。所以寻根溯源,我们来看看 c++allocator.h。这个文件放在了i386-redhat-linux/bits/下面,看似平台相关。打开这个文件一看,结果发现最终包含的文件是ext/new_allocator.h。这还没完,这个文件一开始包含了<new>。终于这个是终点开始看代码。总结一下,基本的include stack是:
new
ext/new_allocator.h
i386-redhat-linux/bits/c++allocator.h
bits/allocator.h
在std的namespace中,首先定义了bad_alloc()类,作为内存分配失败时返回的对象。
class bad_alloc : public exception
{
public:
bad_alloc() throw() { }
virtual ~bad_alloc() throw();
};
这个类继承了exception,同时有一个虚析构函数,表示这个类会被用来继承。
struct nothrow_t { };
extern const nothrow_t nothrow;
这个空结构nothrow被声明成全局的变量,具体用处还不得而知。
typedef void (*new_handler)();
new_handler set_new_handler(new_handler) throw();
这个就是大家熟知的new_handler了,这里定义了这个函数指针的类型。随后就定义了set_new_handler函数,输入一个新的handler,然后返回旧的handler。
随后,声明了各个版本的new和delete操作符,包括分配/释放单个对象和对象数组的版本,以及不抛出任何异常的版本:
void* operator new(std::size_t) throw (std::bad_alloc);
void* operator new[](std::size_t) throw (std::bad_alloc);
void operator delete(void*) throw();
void operator delete[](void*) throw();
void* operator new(std::size_t, const std::nothrow_t&) throw();
void* operator new[](std::size_t, const std::nothrow_t&) throw();
void operator delete(void*, const std::nothrow_t&) throw();
void operator delete[](void*, const std::nothrow_t&) throw();
从上面可以看到,刚才定义的nothrow_t类型被用作不抛出异常版本的第二个参数用以在signature上区分两种operator。
最后,文件定义了两个默认的new/delete placement。new操作符直接返回了传入的第二个参数指向的内存。
inline void* operator new(std::size_t, void* __p) throw() { return __p; }
inline void* operator new[](std::size_t, void* __p) throw() { return __p; }
inline void operator delete (void*, void*) throw() { }
inline void operator delete[](void*, void*) throw() { }
这些operator和placement都是定义在std namespace之外的,所以是全局的内存分配操作符。(操作符只是做了声明,没有具体的实现)
接下来,我们看看ext/new_allocator.h。这个函数是定义在__gnu_cxx namespace里面的,所以是generic的c++实现。这个文件主要定义了一个类 new_allocator。代码为:
template<typename _Tp>
class new_allocator
{
public:
//定义一些类型
typedef size_t size_type;
typedef ptrdiff_t difference_type;
typedef _Tp* pointer;
typedef const _Tp* const_pointer;
typedef _Tp& reference;
typedef const _Tp& const_reference;
typedef _Tp value_type;
template<typename _Tp1>
struct rebind
{ typedef new_allocator<_Tp1> other; };
new_allocator() throw() { }
new_allocator(const new_allocator&) throw() { }
//拷贝构造函数,实现为空
template<typename _Tp1>
new_allocator(const new_allocator<_Tp1>&) throw() { }
~new_allocator() throw() { }
//返回参数对象的地址
pointer
address(reference __x) const { return &__x; }
const_pointer
address(const_reference __x) const { return &__x; }
//调用全局的new操作符分配__n个_Tp对象大小的内存,当__n为0的时候,行为取决于全局操作符的行为
pointer
allocate(size_type __n, const void* = 0)
{ return static_cast<_Tp*>(::operator new(__n * sizeof(_Tp))); }
//释放内存,如果__p为空,由全局delete操作符handle
void
deallocate(pointer __p, size_type)
{ ::operator delete(__p); }
//返回一次最多可以用allocate分配的_Tp对象的个数
size_type
max_size() const throw()
{ return size_t(-1) / sizeof(_Tp); }
//调用全局的new expression构造一个_Tp对象,然后用__val做拷贝构造
void
construct(pointer __p, const _Tp& __val)
{ ::new(__p) _Tp(__val); }
//调用析构函数释放__Tp对象
void
destroy(pointer __p) { __p->~_Tp(); }
};
在i386-redhat-linux/bits/c++allocator.h中只有两行。首先是包含了ext/new_allocator.h, 然后是将在这个文件里面定义的__gnu_cxx::new_allocator typedef 成为___glibcxx_base_allocator,在bits/allocator.h里面使用。
到现在为止,一个基本的内存分配类定义好了(new_allocator,含有allocate,deallocate,construct,destroy几个基本函数),虽然都是调用底层默认的operator new或者new expression。今天太晚了就先写到这里,下次我们分析一下bits/allocator.h。