STL-空间配置器

STL空间配置器的底层原理:

维护了一个狭义的内存池,并且用一个自由链表来维护该内存池。该自由链表比较类似于哈希的开链法的存储结构。


源代码:

#pragma once

using namespace std;

#define __THROW_BAD_ALLOC  cerr << "out of memory" << endl; exit(1)

typedef void*(*FUNC_PTR)();

template <int inst>
class __MallocAllocTemplate   //一级空间配置器
{
public:
	static void* Allocate(size_t n)
	{
		void* result = malloc(n);
		if (result == 0)
		{
			result = OomMalloc(n);
		}
		return result;
	}

	static void Deallocate(void* p)
	{
		free(p);
	}

	static void* Reallocate(void* p, size_t n)//n代表新的空间大小
	{
		void* result = realloc(n);
		if (result == 0)
		{
			result = OomRealloc(p, n);
		}
		return result;
	}

	static FUNC_PTR SetMallocHandler(FUNC_PTR f) //static void (* SetMallocHandler(void (*f)()))()
	{
		FUNC_PTR old = __mallocAllocOomHandler;
		__mallocAllocOomHandler = f;
		return old;
	}
private:
	//函数声明
	static void* OomMalloc(size_t);
	static void* OomRealloc(void *, size_t);
	//静态成员变量
	static FUNC_PTR __mallocAllocOomHandler;
};

//静态变量的初始化
template<int inst>
FUNC_PTR __MallocAllocTemplate<inst>::__mallocAllocOomHandler = 0;

template<int inst>
void* __MallocAllocTemplate<inst>::OomMalloc(size_t n)
{
	FUNC_PTR myMallocHandler;
	void* result;

	while (1)
	{
		myMallocHandler = __mallocAllocOomHandler;
		if (0 == myMallocHandler)
			__THROW_BAD_ALLOC;//抛出异常
		myMallocHandler();
		result = malloc(n);
		if (result)
			return result;
	}
}

template<int inst>
void* __MallocAllocTemplate<inst>::OomRealloc(void* p, size_t)
{
	FUNC_PTR myMallocHandler;
	void* result;

	while (1)
	{
		myMallocHandler = __mallocAllocOomHandler;
		if (0 == myMallocHandler)
			_THROW_BAD_ALLOC;//抛出异常
		myMallocHandler();
		result = realloc(p, n);
		if (result)
			return result;
	}
}

typedef __MallocAllocTemplate<0> MallocAlloc;


template <bool threads, int inst>  //二级空间配置器
class __DefaultAllocTemplate 
{
public:
	static void* Allocate(size_t n)
	{
		obj** myFreeList;
		obj* result;

		if (n > __MAX_BYTES) //如果大小超过128直接调用一级空间配置器(认为超过128的都是大内存)
		{
			return MallocAlloc::Allocate(n);
		}

		myFreeList = _freeList + FREELIST_INDEX(n);

		result = *myFreeList;
		if (result == NULL)//代表该位置还没挂有空闲的空间,然后去申请
		{
			void* r = _Refill(ROUND_UP(n));//n需要8字节对齐
			return r;
		}
		else
		{
			//头删
			*myFreeList = result->_freeListLink;
			return result;
		}
	}

	static void Deallocate(void* p, size_t n)
	{
		obj** myFreeList;
		
		if (n > __MAX_BYTES)
		{
			MallocAlloc::Deallocate(p);
		}

		myFreeList = _freeList + FREELIST_INDEX(n);
		//头插
		((obj*)p)->_freeListLink = *myFreeList;
		*myFreeList = (obj*)p;
	}
private:
	static size_t ROUND_UP(size_t bytes)  //8字节向上对齐
	{
		return (((bytes)+__ALIGN - 1) & ~(__ALIGN - 1));
	}

	static size_t FREELIST_INDEX(size_t bytes)//定位
	{
		return (((bytes)+__ALIGN - 1) / __ALIGN - 1);
	}
private:
	//变量
	enum { __ALIGN = 8 };  //默认最小的内存块是8个字节
	enum { __MAX_BYTES = 128 };//默认最大的内存块是128个字节
	enum { __NFREELISTS = __MAX_BYTES / __ALIGN };//自由链表的个数

	union obj 
	{
		union obj* _freeListLink;
		char _clientData[1];    
	};

	static char* _startFree;  //内存池的头指针 
	static char* _endFree;    //内存池的头尾指针
	static size_t _heapSize;  //内存池总共有多少空间 

	static obj* _freeList[];  //指针数组(自由链表的指针数组)
	
	//函数
	static void* _Refill(size_t n); //对大块内存进行分割并且挂在自由链表下 
	static char* _ChunkAlloc(size_t size, int &nobjs);//开辟大块内存 

};

template <bool threads, int inst>
char* __DefaultAllocTemplate<threads, inst>::_startFree = NULL;

template <bool threads, int inst>
char* __DefaultAllocTemplate<threads, inst>::_endFree= NULL;

template <bool threads, int inst>
size_t __DefaultAllocTemplate<threads, inst>::_heapSize = 0;

template <bool threads, int inst>
typename __DefaultAllocTemplate<threads, inst>::obj* __DefaultAllocTemplate<threads, inst>::_freeList[] = { NULL };
//此处加typename是因为obj是类中的一个类型,要找到它必须先找到__DefaultAllocTemplate<threads, inst>,
//如果不加typename编译器将无法找到__DefaultAllocTemplate<threads, inst>这个类型中的obj类型
//其目的是为了申明__DefaultAllocTemplate<threads, inst>是一个类型 



//填充自由链表
template <bool threads, int inst>
void* __DefaultAllocTemplate<threads, inst>::_Refill(size_t n)  //将分配好的大块内存的第一个返回,将剩下的挂起来
{
	int nobjs = 20;
	char* chunk = _ChunkAlloc(n, nobjs);//申请大块内存 

	obj** my_free_list;
	obj* cur;
	obj* next;
	obj* result;

	if (nobjs == 1)
		return chunk;

	result = (obj*)chunk; // 将第一个chunk返回

	my_free_list = _freeList + FREELIST_INDEX(n); //定位

	*my_free_list = next = (obj*)(chunk + n);//将第二个chunk加入到自由链表中

	//将剩下的挂起来
	for (int i = 2; i < nobjs; ++i) // 第一个返回,第二个被填充在指针数组中,所以从2开始全部挂起来 
	{
		cur = next;
		next = (obj*)((char*)next + n);
		//尾插
		cur->_freeListLink = next;
	}
	next->_freeListLink = NULL; 

	return result;
}

//进行内存块的分配
template <bool threads, int inst>
char* __DefaultAllocTemplate<threads, inst>::_ChunkAlloc(size_t size, int &nobjs) //分配大块内存
{
	size_t BytesLeft = _endFree - _startFree; //内存池剩余的空间大小
	size_t TotelBytes = size*nobjs;//当前需要的申请的内存大小
	char* result;

	if (BytesLeft >= TotelBytes) // 剩余的空间足够
	{
		result = _startFree;
		_startFree += TotelBytes;
		return result;
	}
	else if (BytesLeft >= size)//剩余的空间不够20个,但是够1个以上
	{
		nobjs = BytesLeft / size;//计算新的能够申请的最大个数 
		TotelBytes = size*nobjs;//计算新的需要的空间大小 

		result = _startFree;
		_startFree += TotelBytes;
		return result;
	}
	else   //剩余的空间还不够一个size
	{
		//先将内存池中剩余的一点空间挂起来
		if (BytesLeft > 0)  //还有一点剩余
		{			
			obj** myFreeList = _freeList + FREELIST_INDEX(TotelBytes);
			
			((obj*)_startFree)->_freeListLink = *myFreeList;
			*myFreeList = (obj*)_startFree;
		}
		
		//申请空间
		size_t bytesToGet = TotelBytes * 2 +  ROUND_UP(_heapSize / 16);  //计算新申请空间的大小

		_startFree = (char*)malloc(bytesToGet); //申请空间

		//申请空间失败,去自由链表里边找更大的内存
		if (_startFree == 0) 
		{
			int i = FREELIST_INDEX(size)+1;
			for (; i <= __NFREELISTS; ++i)
			{
				if (_freeList[i] != NULL)//如果找到,则将这块内存交给内存池来管理分配 
				{
					_startFree = (char*)_freeList[i];
					_freeList[i] = _freeList[i]->_freeListLink;
					_endFree = _startFree + (i+1)*__ALIGN;
					return _ChunkAlloc(size, nobjs);
				}
			}

			//山穷水尽
			_endFree = 0;
			_startFree = (char*)MallocAlloc::Allocate(bytesToGet); //如果还失败,_startFree接受一个值为0
		}

		//空间申请成功
		_heapSize += bytesToGet;
		_endFree = _startFree + bytesToGet;
		return _ChunkAlloc(size, nobjs);
	}
}


#ifdef __USE_MALLOC
typedef MallocAlloc alloc;
#else
typedef __DefaultAllocTemplate<false, 0> alloc;
#endif //__USE_MALLOC


template<class T, class Alloc>
class SimpleAlloc
{
public:
	static T* Allocate(size_t size)
	{
		return 0 == size ? 0 : Alloc::Allocate(size*sizeof(T));
	}

	static T* Allocate(void)
	{
		return (T*)Alloc::Allocate(sizeof(T));
	}

	static void Deallocate(T* p, size_t size)
	{
		if (0 != size)
		{
			Alloc::Deallocate(p, size*sizeof(T));
		}
	}

	static void Deallocate(T *p)
	{
		Alloc::deallocate(p, sizeof(T));
	}
};


#include<vector>
using namespace std;
void test()
{
	SimpleAlloc<int, alloc> sa;

	vector<int*> v;
	for (int i = 0; i < 20; ++i)
	{
		v.push_back(sa.Allocate());
	}

	v.push_back(sa.Allocate());
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值