Saturday, March 9, 2013

C++_Smart Pointer_Naked Pointer_Note

當使用到 new這個關鍵字時,在C++則必須一定要做Delete的動作(Release),防止產生Memory leak。

Naked Pointer:當使用 naked Pointer,開發時必須記住物件A所控制、產生的指標當物件A被刪除時裡面的指標要做釋放。

EX:在一個關卡,假設有一個EnemyManager控制所有的Enemy物件,當玩家過關要前往下一關,則EnemyManager必需要被釋放(因為玩家過關後這關卡就不需要有敵人),

如果EnemyManager有宣告並使用到Naked Pointer,開發者在釋放EnemyManager時必須要先做naked Pointer的釋放最後才能釋放EnemyManager,

(在EnemyManager有使用到new的關鍵字的指標都必須要先執行Delete的動作),對開發者來說這件事是非常麻煩、會忘記&難以察覺。

 

Smart Pointer:和Naked Pointer相反,擁有Reference Counting的資訊,讓一個物件被SmartPointer控制,知道每個指向他的Reference。(簡單來說就是Memory Management)


Reference Counting:

在DirectX的物件實做COM時,裡面用到的Reference Counting,AddRef()和Release():

   1: MySound *sound = new MySound;
   2: sound->AddRef();

Reference Counting裡面存有 interge counter在呼叫AddRef()時,interge counter會+1,呼叫Release()則是減少 reference counter並當counter變成0的時候會摧毀物件。



   1: for(int i =0; i<soundNum;++i)
   2: {
   3:     MySound *s = GetSoundPointer(i);
   4:     s->AddRef();
   5:     
   6:     DoFunction();
   7:     if(s->IsPlaying())
   8:     {
   9:         Do();
  10:     }
  11:     s->Release();
  12: }

如果DoFunction()時做遊戲邏輯後,不小心刪除 MySound物件,在這個loop也可能會執行成功即使MySound的Pointer已指向 unallocated memory,這很容易產生 memory corruption。





Smart Pointer:


SmartPtr.h:



   1: #ifndef _SMARTPTR_H_
   2: #define _SMARTPTR_H_
   3: template<class T> class SmartPtr;
   4:  
   5: template<class T>class IRefCount{
   6:     friend class SmartPtr<T>;
   7: protected:
   8:     virtual void AddRef() = 0;
   9:     virtual void Release() = 0;
  10:     virtual T * GetPtr() const =0;
  11: };
  12:  
  13: template<class T> class IRefCountImpl : public IRefCount<T>
  14: {
  15: private :
  16:     int m_Count;
  17: protected:
  18:     virtual void AddRef(){m_Count++;}
  19:     virtual void Release()
  20:     {
  21:     assert(m_Count > =0);
  22:         m_Count--;
  23:         if(m_Count <=0)
  24:             Destory();
  25:     }
  26:     virtual T *GetPtr() const{return((T*)this);}
  27:     virtual void Destory(){if(GetPtr()!=NULL) delete GetPtr();}
  28:     IRefCountImpl(){m_Count=0;}
  29: }
  30:  
  31: template<class T> class SmartPtr
  32: {
  33: private:
  34:     IRefCount<T> *m_RefCount;
  35:     
  36:     class RefCounter :public IRefCountImpl<T>
  37:     {
  38:     private:
  39:         T *m_Ptr;
  40:     protected:
  41:         virtual T* GetPtr() const {return m_Ptr;}
  42:         virtual void Destory(){delete this;}
  43:     public:
  44:         RefCounter(T *ptr){m_Ptr = ptr;}
  45:         virtual ~RefCounter(){IRefCountImpl<T>::Destory();}
  46:     };
  47:     void Assign(void * ptr)
  48:     {
  49:         if(ptr==NULL)
  50:             Assign((IRefCount<T> *)NULL);
  51:         else
  52:             Assign(new RefCounter(static_cast<T *>(ptr)));
  53:     }
  54:     void Assign(IRefCount<T> *refcount)
  55:     {
  56:         if(refcount!=NULL)
  57:             refcount->AddRef();
  58:         IRefCount<T> *oldref = m_RefCount;
  59:         m_RefCount = refcount;
  60:         if(oldref != NULL)
  61:             oldref->Release();
  62:     }
  63:     public:
  64:         SmartPtr()    {m_RefCount = NULL;}
  65:         SmartPtr(T *ptr {m_RefCount= NULL;Assign(ptr);}
  66:         SmartPtr(const SmartPtr &sp){m_RefCount = NULL;Assign(sp.m_RefCount);}
  67:         virtual ~SmartPtr(){Assign((IRefCount<T> *)NULL);
  68:         
  69:         T *GetPtr() const{return (m_RefCount==NULL)?NULL:m_RefCount->GetPtr();}
  70:         
  71:         SmartPtr& operator = (const SmartPtr &s[){Assign(sp.m_RefCount); return *this;
  72:         SmartPtr& operator = (T * ptr){Assign(ptr);return *this;}
  73:         T * operator ->() {assert(GetPtr() != NULL);return GetPtr();}
  74:         operator T* () const {return GetPtr();}
  75:  
  76:         bool operator !() {return GetPtr()== NULL;}
  77:         bool operator ==(const SmartPtr &sp){return GetPtr()==sp.GetPtr();}
  78:         bool operator !=(const SmartPtr &sp){return GetPtr()!=sp.GetPtr();}
  79: };
  80: #endif

Example to use SmartPointer:


SmartPtr.cpp



   1: #include "stdafx.h"
   2: #include "assert.h"
   3: #include "SmartPtr.h"
   4:  
   5: class CMyObject
   6: {
   7:     char *name;
   8: public:
   9:     CMyObject(char *aname){name = aname;printf("create %s\n",name);}
  10:     virtual ~CMyObject(){printf("delete %s\n",name);}
  11:     void print(){printf("print %s\n",name);}
  12: };
  13:  
  14: SmartPtr<CMyObject> f1(char *name)
  15: {
  16:     return SmartPtr<CMyObject>(new CMyObject(name));
  17: }
  18: void f2(CMyObject *o)
  19: {
  20:     printf("(print from a function)");
  21:     o->print();
  22: }
  23:  
  24: int main(void)
  25: {
  26:     SmartPtr<CMyObject> ptr1(new CMyObject("1"));
  27:     SmartPtr<CMyObject> ptr2 = new CMyObject("2");
  28:     
  29:     ptr1 = ptr2; //destory object 1
  30:     ptr2 =f1("3"); // used as a return value
  31:     ptr2 = NULL; //destory object 3
  32:     f2(ptr1);
  33:  
  34:     //Bad Usageage
  35:     //CMyObject o1;
  36:     //ptr1 = &o1;   //It's on stack
  37:  
  38:     //CMyObject *o2 = new CMyObject;
  39:     //ptr1 =o2;
  40:     //ptr2 = o2; //Don't ! unless CMyObject implements IRefCount
  41:                  //try to use ptr1= ptr2 instead.it's always safe;
  42:  
  43:     SmartPtr<int> a (new int);
  44:     SmartPtr<int> b(new int);
  45:     *a=5;
  46:     *b =6
  47:     return
  48: }

Template類別用Overloaded assignment operators去呼叫AddRef()&Release()。


SmartPointer優點:


1.不用擔心Memory leak


 


Reference: Game Coding Complete.

No comments:

Post a Comment