C++ allocator

本文转自:http://www.cnblogs.com/wpcockroach/archive/2012/05/10/2493564.html

感谢该博友分享!


说一说C++里的allocator。我们知道,C++ STL里定义了很多的容器(containers),每一个容器的第二个模板参数都是allocator类型。比方说在VC10里,vector类的模板声明为:

  template<class _Ty, class _Ax = allocator<_Ty> >
  class vector

但是,基本上很少有人会自定义一个allocator。一来,默认的allocator已经够用了;二来,确实不知道该怎么用。一般来说,我们没有必要重新定义一个allocator。自定义的方式主要是为了提高内存分配相关操作的性能。而STL提供的方式性能已经足够好了。事实上,在windows平台上,new的底层实现是基于C语言的malloc函数;malloc函数家族又是基于Windows HeapCreate、HeapAlloc、HeapFree等相关API来实现的(具体可以参考%VSInstallFolder%\VC\crt\src目录中的heapinit.c、malloc.c和new.cpp等相关函数)。

先撇开性能的问题不说,我们看一看如何实现一个自己的allocator。

在C++ 2003标准文档里,关于allocator的说明其实并不多,大概就20.1.5 Allocator requirements20.4.1 The default allocator两处主要位置。虽然内容不多,但是足够我们写出一个自己的allocator。

根据Allocator requirements我们需要提供一些typedefs:

template <typename T>
	class CHxAllocator
{
public:
	// typedefs...
	typedef T                   value_type;
	typedef value_type*         pointer;
	typedef value_type&         reference;
	typedef value_type const*   const_pointer;
	typedef value_type const&   const_reference;
	typedef size_t              size_type;
	typedef ptrdiff_t           difference_type;
	
	// rebind...
	template <typename _other> struct rebind { typedef CHxAllocator<_other> other; };
};

在这里有一个比较不太容易理解的东西:rebind。C++标准里这么描述rebind的:

The member class template rebind in the table above is effectively a typedef template: if the name Allocator is bound to SomeAllocator<T>, then

Allocator::rebind<U>::other is the same type as SomeAllocator<U>.

啥意思?可以用一个简单的例子来说明下:
学校都学过数据结构,比方说栈、单向列表、树。我们就拿栈和列表来对比,看看有什么大不一样的地方。撇开数据结构上的差异,从allocator的角度来看,我们可以发现:堆栈是存贮元素本身的,但是列表实际上不是直接存储元素本身的。要维护一个列表,我们至少还需要一个所谓的next的指针。因此,虽然是一个保存int的列表list<int>,但是列表存储的对象并不是int本身,而是一个数据结构,它保存了int并且还包含指向前后元素的指针。那么,list<int, allocator<int>>如何知道分配这个内部数据结构呢?毕竟allocator<int>只知道分配int类型的空间。这就是rebind要解决的问题。通过allocator<int>::rebind<_Node>()你就可以创建出用于分配_Node类型空间的分配器了。
接下来要提供其他的接口。根据The default allocator的描述,我们要提供如下一些接口:

pointer address(reference val) const
const_pointer address(const_reference val) const
返回val的地址
pointer allocate(size_type cnt, CHxAllocator<void>::const_pointer pHint = 0)分配空间。类似malloc。pHint可以无视,主要是给类库使用,用于提高性能。
void deallocate(pointer p, size_type n)释放空间,类似free。
size_type max_size() const throw()可分配的最大数量。
void construct(pointer p, const_reference val)在地址p所指向的空间,使用val进行填充。需要使用到palcement new,以便保证调用到构造函数。
void destroy(pointer p)析构p指向的内存块中内容。一般通过显示调用析构函数来执行。
allocator() throw ()
allocator(const_reference) throw ()
template <typename _other> allocator(CHxAllocator <_other> const&) throw()
~CHxAllocator() throw()
各种构造函数和析构函数

如何实现上面这些函数,你只要照抄标准库中的实现就可以了。如果你想要用c的malloc和free来实现,也可以这么写:

pointer allocate(size_type cnt, CHxAllocator<void>::const_pointer pHint = 0)
{
	UNREFERENCED_PARAMETER(pHint);
	
	if (cnt <= 0)
	{
		return 0 ;
	}
	
	void* pMem = nullptr ;
	if (max_size() < cnt || (pMem = malloc(cnt * sizeof(value_type))) == NULL)
	{
		throw std::bad_alloc(0);
	}
	
	return static_cast <pointer>(pMem);
}
 
void deallocate(pointer p, size_type)
{
	free(p);
}

void construct(pointer p, const_reference val)
{
	:: new ((void *)p) T(val);
}

void destroy(pointer p)
{
	p->~T();
}

基本上,我们就简单实现了一个自己的allocator。另外,除了这些最主要的接口函数,你还需要实现比较操作符==和!=,但是这些函根据标准文档,都直接返回true和false。

开头已经说了,重写allocator的主要目的是为了提高性。那怎样才能提高性能呢?直接使用Windows的HeapXXXX堆内存API?其实,你自己用一下就会发现,性能提升并不明显。因为通过new,再通过malloc,最后通过HeapAlloc不比直接调用HeapAlloc多几句话。如何实现一个高性能的allocator,需要借助memory pool的想法。另外,侯捷的stl源码剖析里分析了SGI STL利用类似想法实现的一个alloc。


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值