本系列博客是由扭曲45原创,欢迎转载,转载时注明出处,http://blog.csdn.net/cg0206/article/details/8271251
Box2d上有两个和栈有关的类,它们分别是b2StackAllocator和b2GrowableStack。
B2StackAllocator主要是为了运行一个步长时满足box2d需要的临时内存空间,作为栈分配器来防止单步堆分配。
B2GrowableStack主要是为了满足动态树b2DynaicTree中光线投射和区域查询所需要的临时内存空间,这个到动态树的时候再做详细研究。
下面我们就来看这两个类是如何实现的。
1、 b2StackAllocator类
首先我们看它头文件b2StackAllocator.h
看下相关常量和结构体的定义:
//栈中内存池的大小 const int32 b2_stackSize = 100 * 1024; // 100k //栈元素的最大数量 const int32 b2_maxStackEntries = 32; //栈实体定义 struct b2StackEntry { char* data; //头指针 int32 size; //大小 bool usedMalloc; //是否使用 };
代码上面有注释,不多说了。
下面看看b2StackAllocator类的定义吧(好期待(⊙o⊙)哦)
// 这是一个栈分配器用于每一步都快速的分配 // 你必须成对使用allocate/free这对函数。 // 如果你使用allocate/free次数不同时,将会出现断言。 class b2StackAllocator { public: /************************************************************************** * 功能描述:构造函数 * 参数说明:(void) * 返 回 值:无 **************************************************************************/ b2StackAllocator(); /************************************************************************** * 功能描述:析构函数 * 参数说明:(void) * 返 回 值:无 **************************************************************************/ ~b2StackAllocator(); /************************************************************************** * 功能描述:申请内存函数 * 参数说明:size :需要申请的内存大小 * 返 回 值:申请的内存头指针 **************************************************************************/ void* Allocate(int32 size); /************************************************************************** * 功能描述:释放内存函数 * 参数说明:p :释放内存的头指针 * 返 回 值:(void) **************************************************************************/ void Free(void* p); /************************************************************************** * 功能描述:获取已申请的内存容量的最大值 * 参数说明:(void) * 返 回 值:曾经进栈过所有元素【不管现在是否出栈】使用的内存容量 **************************************************************************/ int32 GetMaxAllocation() const; private: //栈的内存池,用于栈子节点的内存开辟 char m_data[b2_stackSize]; //在栈的内存池中,已使用的内存大小 int32 m_index; //栈中的所有元素使用内存大小 int32 m_allocation; //曾经进栈过所有元素【不管现在是否出栈】使用的内存容量 //注意该变量在对象销毁之前只增不减 int32 m_maxAllocation; //栈实体的数组 b2StackEntry m_entries[b2_maxStackEntries]; //栈中元素的数量 int32 m_entryCount; };
同样不多说了,看注释。我们再来看看b2StakAllocator是如何实现的:
b2StackAllocator::b2StackAllocator() { //初始化相关变量 m_index = 0; m_allocation = 0; m_maxAllocation = 0; m_entryCount = 0; } b2StackAllocator::~b2StackAllocator() { //验证内存是否已完全释放 b2Assert(m_index == 0); //验证元素是否已完全出栈 b2Assert(m_entryCount == 0); }
下面是Allocate、Free、GetMaxAllocation函数,看代码:
void* b2StackAllocator::Allocate(int32 size) { //验证栈中元素的有效性,防止内存溢出 b2Assert(m_entryCount < b2_maxStackEntries); //获取栈实体头指针 b2StackEntry* entry = m_entries + m_entryCount; //实体大小 entry->size = size; //当内存池m_data已使用的大小加需要申请的大小大于内存池的总容量时,则在堆上申请 if (m_index + size > b2_stackSize) { //申请大小为size的内存,并标记是在堆上申请的 entry->data = (char*)b2Alloc(size); entry->usedMalloc = true; } else { //从m_data中获取内存,并标记不是在堆上申请的 //同时修改m_index的值 entry->data = m_data + m_index; entry->usedMalloc = false; m_index += size; } //增加栈中的所有元素使用内存大小 m_allocation += size; //修改内存容量的最大值 m_maxAllocation = b2Max(m_maxAllocation, m_allocation); //增加栈中元素的数量 ++m_entryCount; //返回栈中元素的内存头指针 return entry->data; } void b2StackAllocator::Free(void* p) { //验证栈中元素的有效性 b2Assert(m_entryCount > 0); //栈中数量减1,在这里用数组模拟了出栈 b2StackEntry* entry = m_entries + m_entryCount - 1; b2Assert(p == entry->data); //是否是在堆上申请的 if (entry->usedMalloc) { //释放p b2Free(p); } else { //将索引值减去栈实体的内存大小 m_index -= entry->size; } //减去已释放的内存大小 m_allocation -= entry->size; //元素数量减少 --m_entryCount; //将指针置空,防止野指针 p = NULL; } int32 b2StackAllocator::GetMaxAllocation() const { return m_maxAllocation; }
对于Allocate函数,我们用原先分配了大小b2_stackSize的内存池辅助数组。同时也用m_index记录了已使用的内存池的大小。若内存池中剩余内存不够则在堆上申请,否则在内存池中获取相应大小的内存。
同样Free函数也有两种情况,在堆上申请的内存直接释放,在内存池中申请的内存返还到内存池中,不用释放。
GetMaxAllocation函数在同一个对象的生命周期里是返回的值是只增不减的。
2、 b2GrowableStack类
本类定义、实现均在b2GrowableStack.h文件中
//这是一个可增长的先进后出初始容量为N的栈 //如果栈的大小达到初始容量,则在堆中增加栈的大小 template <typename T, int32 N> class b2GrowableStack { public: /************************************************************************** * 功能描述:栈构造函数,初始化相关数据 * 参数说明:(void) * 返 回 值:(void) **************************************************************************/ b2GrowableStack() { m_stack = m_array; m_count = 0; m_capacity = N; } /************************************************************************** * 功能描述:栈析构函数,释放相关内存 * 参数说明:(void) * 返 回 值:(void) **************************************************************************/ ~b2GrowableStack() { if (m_stack != m_array) { b2Free(m_stack); m_stack = NULL; } } /************************************************************************** * 功能描述:进栈操作 * 参数说明:element :进栈元素 * 返 回 值:(void) **************************************************************************/ void Push(const T& element) { //栈已满 if (m_count == m_capacity) { //获取栈头指针并保存到old中 T* old = m_stack; //将栈的容量扩充到原来的2倍 m_capacity *= 2; //申请内存,并保存到m_stack中 m_stack = (T*)b2Alloc(m_capacity * sizeof(T)); //将原来的内容整体拷贝到m_stack中去 std::memcpy(m_stack, old, m_count * sizeof(T)); if (old != m_array) { //释放old指向的内存 b2Free(old); } } //进栈,并将元素个数自加 m_stack[m_count] = element; ++m_count; } /************************************************************************** * 功能描述:出栈操作 * 参数说明:(void) * 返 回 值:返回最近进入栈的值 **************************************************************************/ T Pop() { //验证元素个数的有效性 b2Assert(m_count > 0); //元素个数自减,并出栈 --m_count; return m_stack[m_count]; } /************************************************************************** * 功能描述:获取栈中元素的个数 * 参数说明:(void) * 返 回 值:栈中元素的个数 **************************************************************************/ int32 GetCount() { return m_count; } private: //栈头指针 T* m_stack; //辅助数组 T m_array[N]; //元素的个量 int32 m_count; //栈的容量 int32 m_capacity; };
该类是个模板类,所有元素的类型和栈的大小均有使用的时候自己定义,同样是由内存池即辅助数组模拟的入栈和出栈。不同地方是当内存池不足时,b2GrowableStack会重新申请一个是原来一倍容量更大的新的内存池,并拷贝旧内存池中的内容到新的内存池中,同时释放除辅助数组以外的旧的内存池,不过多次扩充内存对将会有一定的影响,最好能够使用之前先估算一下所需内存池的大小。对于push/pop函数,均用数组模拟的进出栈。其他也没啥好说的了。