LightSTL:(2)最简版 内存分配器的设计

内存分配器 Allocator 是为容器元素配置内存空间的类,从使用者的角度来说,Allocator一般隐藏在幕后,且无特殊需要,不会关注它。
然而,Allocator是容器创建的基石,是自己实现STL容器必不可少的第一步,同时,Allocator的设计会影响容器使用的效率。
在这里,为了尽快进入STL更加激动人心的实现部分,先仅仅实现了一个最简版本的 default allocator,后续可以在该版本之上再针对Allocator进行优化和扩充。

STL定义的Allocator的接口

STL对Allocator进行了规定,给出了Allocator的allocator 标准接口,可以参见cplusplus网站的说明。在这里,列出如下:

1.模板定义

template
class allocator;

2. 类型定义

member definition comment
value_type T 元素类型
pointer T* 元素指针
reference T& 引用
const_pointer const T* 常量指针
const_reference const T& 常量引用
size_type size_t 元素数量
difference_type ptrdiff_t 两个指针之间的距离
rebind member class Its member type other is the equivalent allocator type to allocate elements of type Type

3. 成员函数

member function comment
(constructor) 可选的构造函数
(destructor) 可选的析构函数
allocate 分配内存
deallocate 释放内存
construct new相应的对象
destroy delete相应的对象
address 返回对象地址,还有const版本
max_size 返回可成功配置的最大量

allocator模板类的实现

1.类型定义

STL定义的接口中,difference_type、reference等名称看起来有些怪怪的,针对一个分配器为何要定义这么复杂的类型?事实上,这些内容和后续的迭代器设计及traits编程技法是相关的,这里定义这些是为了和迭代器等类保持一致。这些变量的定义直接用typedef关键字即可。

	template <class T>
	class allocator
	{
	public:
		typedef T		   value_type;
		typedef T*         pointer;
		typedef const T*   const_pointer;
		typedef T&         reference;
		typedef const T&   const_reference;
		typedef size_t     size_type;
		typedef ptrdiff_t  difference_type;

		//rebind allocator of type U.   
		template <class U>
		struct rebind
		{  typedef allocator<U> other; };
		
		//... funcs
	}

2. 成员函数定义

在规定的成员函数中,最重要的四个接口是allocate()deallocate()construct()destory(),其中前两个负责空间的分配与释放,后两个负责对象的构造与析构。为何要将空间操作和对象操作分开呢?
事实上,我们比较习惯的C++内存分配方式是基于newdelete,如下方式,newdelete都包含空间和对象的操作。

class A
{	.... /*data and funcs*/  };
A* obj = new A();   //new中包含 空间配置 和 对象构造
delete A;           //delete中包含对象析构和空间释放

但是对于泛化的情况来说,空间操作和对象操作一起操作在某些情况下可能效率很低,因此出于效率的考虑,将这两部分独立开来设计。
虽然有这样的考虑,但是在这里为了设计最简单的分配器,在具体实现上并没有考虑太多,仅仅对newdelete进行了简单的包装,可能效率还更低一点,但是在这样的架构下,优化的潜力是不可估量的。
为了程序的复用性,在这里,首先给出了函数模板形式的_allocate()_deallocate()_construct()_destory()
其中,_construct()利用C++的 placement new 运算子将初值设定到指针所指的空间上。

	template <class T>
	inline T* _allocate(ptrdiff_t size, T*)
	{
		std::set_new_handler(0);
		T* tmp = (T*)(::operator new((size_t)(size * sizeof(T))));
		if (tmp == 0)
		{
			std::cerr << "out of memeory" << std::endl;
			exit(1);
		}
		return tmp;
	}

	template <class T>
	inline void _deallocate(T* buffer)
	{ ::operator delete(buffer);}

	template <class T1, class T2>
	inline void _construct(T1* p, const T2& value)
	{ new(p) T1(value); } // placement new.invoke constructor of T1

	template <class T>
	inline void _destory(T* ptr)
	{ ptr->~T(); }

然后在allocator中对它们进行了调用,在这里,allocator作为一个工具类,其函数要定义为静态的,方便以allocator<T>::的形式调用。

	template <class T>
	class allocator
	{
	public:
		//...
		static pointer allocate(size_type n, const void* hint=0)
		{ return _allocate((difference_type)n, (pointer)0); }

		static void deallocate(pointer p)
		{ _deallocate(p); }

		static void construct(pointer p, const T& value)
		{ _construct(p, value); }

		static void destory(pointer p)
		{ _destory(p); }
	}

allocator除了上面的几个函数,还有address()max_size(),给出如下:

		static pointer address(reference x)
		{ return (pointer)&x; }

		static const_pointer const_address(const_reference x)
		{ return (const_pointer)&x; }

		static size_type max_size()
		{ return size_type(UINT_MAX / sizeof(T)); }

全部代码 Allocator.h

下面给出全部的代码

#ifndef ALLOCATOR_H
#define ALLOCATOR_H

#include <new>      //for placement new
#include <cstddef>  //for ptrdiff_t, size_t
#include <cstdlib>  //for exit()
#include <climits>  //for UNIT_MAX
#include <iostream> //for cerr

namespace LightSTL
{
	template <class T>
	inline T* _allocate(ptrdiff_t size, T*)
	{
		std::set_new_handler(0);
		T* tmp = (T*)(::operator new((size_t)(size * sizeof(T))));
		if (tmp == 0)
		{
			std::cerr << "out of memeory" << std::endl;
			exit(1);
		}
		return tmp;
	}

	template <class T>
	inline void _deallocate(T* buffer)
	{ ::operator delete(buffer);}

	template <class T1, class T2>
	inline void _construct(T1* p, const T2& value)
	{ new(p) T1(value); } // placement new.invoke ctor of T1

	template <class T>
	inline void _destory(T* ptr)
	{ ptr->~T(); }

	template <class T>
	class allocator
	{
	public:
		typedef T		   value_type;
		typedef T*         pointer;
		typedef const T*   const_pointer;
		typedef T&         reference;
		typedef const T&   const_reference;
		typedef size_t     size_type;
		typedef ptrdiff_t  difference_type;

		//rebind allocator of type U.   
		template <class U>
		struct rebind
		{  typedef allocator<U> other; };

		//hint used for locality.
		static pointer allocate(size_type n, const void* hint=0)
		{ return _allocate((difference_type)n, (pointer)0); }

		static void deallocate(pointer p)
		{ _deallocate(p); }

		static void construct(pointer p, const T& value)
		{ _construct(p, value); }

		static void destory(pointer p)
		{ _destory(p); }

		static pointer address(reference x)
		{ return (pointer)&x; }

		static const_pointer const_address(const_reference x)
		{ return (const_pointer)&x; }

		static size_type init_page_size()
		{ return max(size_type(1), size_type(4096 / sizeof(T))); }

		static size_type max_size()
		{ return size_type(UINT_MAX / sizeof(T)); }
	};

}//end of namespace

#endif // !ALLOCATOR_H

Test case:

	int* ia = LightSTL::allocator<int>::allocate(1);
	LightSTL::allocator<int>::construct(ia, 5);
	std::cout << *ia << std::endl;

	std::string* istr = LightSTL::allocator<std::string>::allocate(1);
	LightSTL::allocator<std::string>::construct(istr, "abcd");
	std::cout << *istr << std::endl;
	//output 
	//5
	//abcd

发布了48 篇原创文章 · 获赞 10 · 访问量 3万+

猜你喜欢

转载自blog.csdn.net/mhywoniu/article/details/104401513