本系列博客是由扭曲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函数,均用数组模拟的进出栈。其他也没啥好说的了。