游戏开发论坛

 找回密码
 立即注册
搜索
查看: 4138|回复: 1

小弟精心打造的一个对象缓冲器-1.2版

[复制链接]

45

主题

157

帖子

169

积分

注册会员

Rank: 2

积分
169
QQ
发表于 2009-8-9 11:52:00 | 显示全部楼层 |阅读模式
    相比1.1版,增加了一个OB_Ptr 代理类(proxy classes),用它来操作申请的对象,防止错误delete申请来的对象指针而引起程序崩溃。这个OB_Ptr类在语法上只能默认构造对象,构造的对象只能进行“->”操作或“(* ).”操作或判断是否为Null指针。不能进行拷贝构造,赋值“=”或“delete”等操作,因为这些操作往往容易引起申请的对象泄漏或非法delete指针等错误。
/*************ObjectBuffer1.2****************/
//对象缓冲器类模版,缓冲器预先分配一定数量的对象,当用到时就从缓冲器
//请求,不用时归还缓冲器,采用页链表分配技术使缓冲器理论上无分配上限

enum ObjectBuffer_ERROR
{
  ObjectBuffer_ERROR_No,
  ObjectBuffer_ERROR_Memory,  //内存分配错误
  ObjectBuffer_ERROR_NullPointer,//无效对象指针
  ObjectBuffer_ERROR_MultipleReturn,//多重归还对象
};
template <typename T>   
class ObjectBuffer
{
private:
  struct SPage;
public:
  //proxy classes
  class OB_Ptr
  {      
  public:
    friend struct SPage;

    OB_Ptr() : m_p( NULL ){}
    ~OB_Ptr(){}
    T* operator->()const{ return m_p; }
    T& operator*()const{ return *m_p; }

    bool IsNull()const{ return NULL == m_p; }
  private:
    OB_Ptr( T* p ){}
    OB_Ptr(const OB_Ptr& rhs){}
    OB_Ptr& operator=(const OB_Ptr& rhs){}
   
    T* m_p;
  };
public:
  ObjectBuffer();
  
  //ulObjectPage,每页上对象的数量,此值应根据对象大小、使用情况统筹选取,以降低内存占用及提高性能。
  ObjectBuffer( unsigned long ulObjectOfPage );
  ~ObjectBuffer();

  //请求一个对象
  bool RequestObject( OB_Ptr& ptr );
  
  //归还一个对象(注意:归后ptr为NULL值)。
  bool ReturnObject( OB_Ptr& ptr );
  
  //获取错误代码
  ObjectBuffer_ERROR GetError()const{ return m_Error; }
        
  enum
  {
    ObjectOfPage_Min = 16,    //每页中对象数量的最小值
  };
private:
  struct SPage
  {
    T*  m_pObjects;  //对象数组
    T** m_ppObjectStack;  //对象堆,堆中存储的是对象指针
    bool*  m_pbIdles; //对象的空闲标志
    unsigned long  m_ulTop;  //堆顶,当为0时说明对象已用完
    unsigned long  m_ulObjectCount;  //对象数量
  SPage*  m_pFrontPage;  //前一页指针
SPage*  m_pBackPage;  //后一页指针
  bool    m_bMemoryError;    //内存分配错误标志
      
  SPage()
  {
    m_pObjects= NULL;  //对象数组
    m_ppObjectStack = NULL;//对象堆,堆中存储的是对象指针
    m_pbIdles= NULL;  //对象的空闲标志
    m_ulTop  = 0;  //堆顶,当为0时说明对象已用完
    m_ulObjectCount = 0;  //对象数量
    m_pFrontPage  = NULL;  //前一页指针
    m_pBackPage    = NULL;  //后一页指针
    m_bMemoryError  = false;  //内存分配错误标志
        
    m_ulObjectCount = ObjectOfPage_Min;
    m_pObjects = new T[ m_ulObjectCount ];
    m_ppObjectStack = new T*[ m_ulObjectCount ];
    m_pbIdles = new bool[ m_ulObjectCount ];
    if( NULL == m_pObjects || NULL == m_ppObjectStack || NULL == m_pbIdles )
    {
      Release();
      m_bMemoryError = true;
      return;
    }
    for( unsigned long i = 0; i < m_ulObjectCount; ++i )
    {
      m_ppObjectStack[m_ulObjectCount - i - 1] = &m_pObjects;
      m_pIdles = true;
    }
    m_ulTop = m_ulObjectCount;
  }
  
  SPage( unsigned long ulObjectOfPage )
  {
    m_pObjects  = NULL;  //对象数组
    m_ppObjectStack = NULL;//对象堆,堆中存储的是对象指针
    m_pbIdles  = NULL;  //对象的空闲标志
    m_ulTop  = 0;  //堆顶,当为0时说明对象已用完
    m_ulObjectCount = ulObjectOfPage;  //对象数量
    m_pFrontPage  = NULL;  //前一页指针
    m_pBackPage    = NULL;  //后一页指针
    m_bMemoryError  = false;  //内存分配错误标志

    if( m_ulObjectCount < ObjectOfPage_Min )m_ulObjectCount = ObjectOfPage_Min;
    m_pObjects = new T[ m_ulObjectCount ];
    m_ppObjectStack = new T*[ m_ulObjectCount ];
    m_pbIdles = new bool[ m_ulObjectCount ];
    if( NULL == m_pObjects || NULL == m_ppObjectStack || NULL == m_pbIdles )
    {
      Release();
      m_bMemoryError = true;
      return;
    }
    for( unsigned long i = 0; i < m_ulObjectCount; ++i )
    {
      m_ppObjectStack[m_ulObjectCount - i - 1] = &m_pObjects;
      m_pbIdles = true;
    }
    m_ulTop = ulObjectOfPage;
  }

  ~SPage(){ Release(); }
  
  void Release()
  {
    if( NULL != m_pObjects )delete[] m_pObjects;
    if( NULL != m_ppObjectStack )delete[] m_ppObjectStack;
    if( NULL != m_pbIdles )delete[] m_pbIdles;
        
    m_pObjects  = NULL;  //对象数组
    m_ppObjectStack = NULL;//对象堆,堆中存储的是对象指针
    m_pbIdles= NULL;  //对象的空闲标志
    m_ulTop  = 0;  //堆顶,当为0时说明对象已用完
    m_ulObjectCount = 0;  //对象数量
    m_pFrontPage  = NULL;  //前一页指针
    m_pBackPage  = NULL;  //后一页指针
    m_bMemoryError    = false;  //内存分配错误标志
  }

  bool IsEmpty(){  return 0 == m_ulTop; }
  bool IsFull(){ return m_ulTop == m_ulObjectCount; }
  bool IsMember( const OB_Ptr& ptr ){ return ( ( ptr.m_p >= m_pObjects ) && ( ptr.m_p <= &m_pObjects[m_ulObjectCount-1] ) ); }
   
  void RequestObject( OB_Ptr& ptr )
  {
    --m_ulTop;
    m_pbIdles[ m_ppObjectStack[ m_ulTop ] - m_pObjects ] = false;
    ptr.m_p = m_ppObjectStack[ m_ulTop ];
  }

  ObjectBuffer_ERROR ReturnObject( OB_Ptr& ptr )
  {
    T* pObject = ptr.m_p;
    if( ( ( ( int )pObject - ( int )m_pObjects ) % sizeof( T ) ) != 0 )
    {
      return ObjectBuffer_ERROR_NullPointer;
    }
    if( IsFull() || m_pbIdles[ pObject - m_pObjects ]  )
    {
      return ObjectBuffer_ERROR_MultipleReturn;
    }
    m_pbIdles[ pObject - m_pObjects ] = true;
    m_ppObjectStack[ m_ulTop ] = pObject;
    ++m_ulTop;
    ptr.m_p = NULL;
    return ObjectBuffer_ERROR_No;
  }
};

SPage*      m_pBeginPage;      //开始页
SPage*      m_pEndPage;        //结束页
SPage*      m_pCurrentPage;      //当前操作页
unsigned long  m_ulPageCount;      //页数量
unsigned long  m_ulObjectOfPage;    //每页中对象的数量
unsigned long  m_ulIdleObjectCount;  //空闲对象数量
ObjectBuffer_ERROR  m_Error;      //错误代码
};
//end

//////////////////////////////////
//ObjectBuffer Class template implement
//////////////////////////////////////

template <typename T>
ObjectBuffer<T>::ObjectBuffer()
{
  m_pBeginPage  = NULL;    //开始页
  m_pEndPage    = NULL;    //结束页
  m_pCurrentPage  = NULL;    //当前操作页
  m_ulPageCount  = 0;    //页数量
  m_ulObjectOfPage = ObjectOfPage_Min; //每页中对象的数量
  m_ulIdleObjectCount = 0;      //空闲对象数量
  m_Error = ObjectBuffer_ERROR_No;  //错误标志
        
  m_pCurrentPage = m_pEndPage = m_pBeginPage = new SPage( m_ulObjectOfPage );
  if( NULL == m_pBeginPage || m_pBeginPage->m_bMemoryError )
  {
    m_Error = ObjectBuffer_ERROR_Memory;
    return;
  }
  m_ulPageCount = 1;
  m_ulIdleObjectCount += m_ulObjectOfPage;//空闲对象数量增加一个页面的数量
}
template <typename T>
ObjectBuffer<T>::ObjectBuffer( unsigned long ulObjectOfPage )
{
m_pBeginPage  = NULL;    //开始页
  m_pEndPage    = NULL;    //结束页
  m_pCurrentPage  = NULL;    //当前操作页
  m_ulPageCount  = 0;    //页数量
    m_ulObjectOfPage = ulObjectOfPage;  //每页中对象的数量
    m_ulIdleObjectCount = 0;      //空闲对象数量
    m_Error = ObjectBuffer_ERROR_No;  //错误标志

    if( m_ulObjectOfPage < ObjectOfPage_Min )m_ulObjectOfPage = ObjectOfPage_Min;
    m_pCurrentPage = m_pEndPage = m_pBeginPage = new SPage( m_ulObjectOfPage );
    if( NULL == m_pBeginPage || m_pBeginPage->m_bMemoryError )
    {
      m_Error = ObjectBuffer_ERROR_Memory;
      return;
    }
    m_ulPageCount = 1;
    m_ulIdleObjectCount += m_ulObjectOfPage;//空闲对象数量增加一个页面的数量
  }
  template <typename T>
  ObjectBuffer<T>::~ObjectBuffer()
  {
    while( NULL != m_pBeginPage )
    {
      m_pCurrentPage = m_pBeginPage;
      m_pBeginPage = m_pBeginPage->m_pBackPage;
      m_pCurrentPage->Release();
      delete m_pCurrentPage;      
    }

    m_pBeginPage  = NULL;    //开始页
    m_pEndPage    = NULL;    //结束页
    m_pCurrentPage  = NULL;    //当前操作页
    m_ulPageCount  = 0;    //页数量
    m_ulObjectOfPage = 0; //每页中对象的数量
    m_ulIdleObjectCount = 0;      //空闲对象数量
    m_Error = ObjectBuffer_ERROR_No;  //错误标志
  }
  
  template <typename T>
  bool ObjectBuffer<T>::RequestObject( OB_Ptr& ptr )
  {
    if( m_Error != ObjectBuffer_ERROR_No )return false;
    if( !ptr.IsNull() )
    {
      if( !ReturnObject( ptr ) )return false;
    }
   
    if( NULL == m_pCurrentPage )m_pCurrentPage = m_pBeginPage;
    for( unsigned long i = 0; i < m_ulPageCount; ++i )
    {
      if( !m_pCurrentPage->IsEmpty() )//如果不为空就请求一个对象
      {
        --m_ulIdleObjectCount;  //一个对象成功分配,空闲对象数量减1
        m_pCurrentPage->RequestObject( ptr );
        return   true;
      }
      if( m_pCurrentPage == m_pEndPage )//如果到了未尾就从头开始
      {
        m_pCurrentPage = m_pBeginPage;
      }
      else
      {
        m_pCurrentPage = m_pCurrentPage->m_pBackPage;
      }
    }

    //如果循环结束还没找到空闲对象,说明页面已经用完,需要分配新页面
    SPage* m_pNew = NULL;
    m_pNew = new SPage( m_ulObjectOfPage );
    if( NULL == m_pNew || m_pNew->m_bMemoryError )
    {
      m_Error = ObjectBuffer_ERROR_Memory;
      return false;
    }
    m_pNew->m_pFrontPage = m_pEndPage;
    m_pEndPage->m_pBackPage = m_pNew;
    m_pEndPage = m_pNew;
    ++m_ulPageCount;
    m_pCurrentPage = m_pEndPage;
    m_ulIdleObjectCount += m_ulObjectOfPage - 1;  //空闲对象数量增加一个页面的数量-1
    m_pCurrentPage->RequestObject( ptr );
    return true;
  }
  
  template <typename T>
  bool ObjectBuffer<T>::ReturnObject( ObjectBuffer<T>::OB_Ptr& ptr )
  {
    if( m_Error != ObjectBuffer_ERROR_No)return false;
    if( ptr.IsNull() )
    {
      m_Error = ObjectBuffer_ERROR_NullPointer;
      return false;
    }
    if( NULL == m_pCurrentPage )m_pCurrentPage = m_pBeginPage;
    for( unsigned long i = 0; i < m_ulPageCount; ++i )
    {
      if( m_pCurrentPage->IsMember( ptr ) )//对象是否属于此页
      {
        ObjectBuffer_ERROR Ret = ObjectBuffer_ERROR_No;
        if( ObjectBuffer_ERROR_No != ( Ret = m_pCurrentPage->ReturnObject( ptr ) ) )
        {
          m_Error = Ret;
          return false;
        }
        ++m_ulIdleObjectCount;  //一个对象成功归还,空闲对象数量加1
        break;
      }
      if( m_pCurrentPage == m_pEndPage )//如果到了未尾就从头开始
      {
        m_pCurrentPage = m_pBeginPage;
      }
      else
      {
        m_pCurrentPage = m_pCurrentPage->m_pBackPage;
      }
    }
  
    //如果空闲对象数量大于2倍页面数量,说明空闲对象太多了,需要释放一些以减少内存占用
    if( m_ulIdleObjectCount >= ( m_ulObjectOfPage + m_ulObjectOfPage ) )
    {
      SPage* pTracker = m_pBeginPage;    //用于追踪
      SPage* pHandlers = NULL;      //用于操作
      //遍历页面链表,找出空闲页面并释放
      while( NULL != pTracker )
      {
        if( m_ulIdleObjectCount <= m_ulObjectOfPage )break;

        pHandlers = pTracker;
        pTracker = pTracker->m_pBackPage;//向后遍历

        //如果为空闲页面
        if( pHandlers->IsFull() )
        {
          //既不是开始页面也不是结束页面
          if( pHandlers != m_pBeginPage && pHandlers != m_pEndPage )
          {
            pHandlers->m_pFrontPage->m_pBackPage = pHandlers->m_pBackPage;
            pHandlers->m_pBackPage->m_pFrontPage = pHandlers->m_pFrontPage;
          }
          else
          {
            //如果是开始页面让开始指针指向下一页面
            if( pHandlers == m_pBeginPage )
            {
              m_pBeginPage = pHandlers->m_pBackPage;//让下一页面作为新的开始页面
              m_pBeginPage->m_pFrontPage = NULL;//让新开始页面的前一页面指向空
            }
            else
            {
              //如果是结束页面让结束指针指向上一页面
              m_pEndPage = pHandlers->m_pFrontPage;//让上一页面作为新的结束页面
              m_pEndPage->m_pBackPage = NULL;//让新结束页面的后一页面指向空
            }
          }
          //如果和当前页面相同让当前页面指向下一页面
          if( pHandlers == m_pCurrentPage )
          {
            m_pCurrentPage = pHandlers->m_pBackPage;
            if( NULL == m_pCurrentPage )m_pCurrentPage = m_pBeginPage; //如果为NULL重新指向开始页面
          }
          pHandlers->Release();
          delete pHandlers;
          --m_ulPageCount;
          m_ulIdleObjectCount -= m_ulObjectOfPage;
        }
      }
    }
    return true;
  }  
  ///////////////////////////////
  //end
  ////////////////////////////////////



用法跟以前没多大差别:
char* ErrorInfo[] = { "ObjectBuffer_ERROR_No——没有错误",
  "ObjectBuffer_ERROR_Memory——内存分配错误",
  "ObjectBuffer_ERROR_NullPointer——无效对象指针",
  "ObjectBuffer_ERROR_MultipleReturn——多重归还对象",
};

const int test_count = 300;

ObjectBuffer<V3D> VecBuff( 400 );
ObjectBuffer<V3D>::OB_Ptr pvs[test_count];
  
for( int i = 0; i < test_count; i++ )
{
  if( !VecBuff.RequestObject( pvs ) || pvs.IsNull() )//申请对象并判断是否为Null指针
  {
    cout<<ErrorInfo[VecBuff.GetError()]<<endl;
    return 0;
  }
  pvs->x = i*10.0f;
  pvs->y = i*i+100.0f;      //"->"操作
  ( *pvs ).z = i*i*i - 5000.0f; //"(* )."操作
}

delete pvs[2]; //这句会引起程序崩溃,但现在编译不会通过。
pvs[1] = new V3D; //这句会引起p[1]原来所指的对象泄漏,还会引起p[1]归还失败,
//但现在编译不会通过。
pvs[0] = pvs[1]; //这句会引起p[0]原来所指的对象泄漏,但现在编译不会通过。
for( i = 0; i < test_count; i++ )
{
  if( !VecBuff.ReturnObject( pvs ) )//归还对象
  {
    cout<<ErrorInfo[VecBuff.GetError()]<<endl;
    return 0;
  }
}

362

主题

3023

帖子

3553

积分

论坛元老

Rank: 8Rank: 8

积分
3553
发表于 2009-8-10 07:10:00 | 显示全部楼层

Re:小弟精心打造的一个对象缓冲器-1.2版

我采用对象池(1级指针的链表)和句柄(2级指针)管理对象。
对象池的容量不会减少,当对象被删除时,池子里的单元(1级指针型)可以被再利用。
句柄(2级指针)指向的目标永远存在,所以不会出错。
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

作品发布|文章投稿|广告合作|关于本站|游戏开发论坛 ( 闽ICP备17032699号-3 )

GMT+8, 2025-6-21 17:52

Powered by Discuz! X3.4

Copyright © 2001-2021, Tencent Cloud.

快速回复 返回顶部 返回列表