智能指針在C++11的標準中已經存在了,分別是unique_ptr,shared_ptr,weak_ptr,其中最常用的應該是share_ptr,它採用引用計數的方式管理內存,當引用計數爲0的時候,自動釋放內存,但是由於shared_ptr考慮到了線程安全,所以會存在有較大的性能損失。所以在實時遊戲開發中,往往不會用到shared_ptr。
在cocos2d-x3.2以及更高的版本中,cocos2d-x提供了自己的智能指針方案——RefPtr,這套方案實際上也是模仿C++11中的shared_ptr機制實現的,他結合了Cocos2d-x自身的引用計數來管理內存,當然爲了性能,他犧牲了線程安全(cocos2d-x的引用計數不支持線程安全)。
下面看看cocos2d-x中智能指針的源碼,首先是構造函數
inline RefPtr()
:
_ptr(nullptr)
{
}
inline RefPtr(RefPtr<T> && other)
{
_ptr = other._ptr;
other._ptr = nullptr;
}
inline RefPtr(T * ptr)
:
_ptr(const_cast<typename std::remove_const<T>::type*>(ptr)) // Const cast allows RefPtr<T> to reference objects marked const too.
{
CC_REF_PTR_SAFE_RETAIN(_ptr);
}
inline RefPtr(std::nullptr_t ptr)
:
_ptr(nullptr)
{
}
inline RefPtr(const RefPtr<T> & other)
:
_ptr(other._ptr)
{
CC_REF_PTR_SAFE_RETAIN(_ptr);
}
RefPtr提供了多個構造函數,可以用默認構造函數聲明一個空智能指針,用別的指針來聲明一個智能指針,也提供了移動構造函數將內存偷過來,複製構造函數保持內存的強引用。構造函數最爲重要的莫過於CC_REF_PTR_SAFE_RETAIN宏了,它是智能指針專用的宏,在外部是引用不到的。實現如下
#define CC_REF_PTR_SAFE_RETAIN(ptr)\
\
do\
{\
if (ptr)\
{\
const_cast<Ref*>(static_cast<const Ref*>(ptr))->retain();\
}\
\
} while (0);
核心就是retain,保持一個強引用。下面是聲明智能指針的用法
//inline RefPtr()
RefPtr<int> a;
//inline RefPtr(T * ptr)
RefPtr<int> b(new int);
//inline RefPtr(const RefPtr<T> & other)
RefPtr<int>c(b);
//inline RefPtr(RefPtr<T> && other)
RefPtr<int>d(std::move(b));
//inline RefPtr(std::nullptr_t ptr)
RefPtr<int>d(nullptr);
接下來看看析構函數
inline ~RefPtr()
{
CC_REF_PTR_SAFE_RELEASE_NULL(_ptr);
}
析構函數就簡單多了,只有一個,具體還是要到宏裏面。
#define CC_REF_PTR_SAFE_RELEASE_NULL(ptr)\
\
do\
{\
if (ptr)\
{\
const_cast<Ref*>(static_cast<const Ref*>(ptr))->release();\
ptr = nullptr;\
}\
\
} while (0);
實際上就是對其release並且置空。
另外,也提供了移動賦值函數以及賦值函數。
inline RefPtr<T> & operator = (RefPtr<T> && other)
{
if (&other != this)
{
CC_REF_PTR_SAFE_RELEASE(_ptr);
_ptr = other._ptr;
other._ptr = nullptr;
}
return *this;
}
inline RefPtr<T> & operator = (T * other)
{
if (other != _ptr)
{
CC_REF_PTR_SAFE_RETAIN(other);
CC_REF_PTR_SAFE_RELEASE(_ptr);
_ptr = const_cast<typename std::remove_const<T>::type*>(other); // Const cast allows RefPtr<T> to reference objects marked const too.
}
return *this;
}
inline RefPtr<T> & operator = (std::nullptr_t other)
{
CC_REF_PTR_SAFE_RELEASE_NULL(_ptr);
return *this;
}
第一個是移動賦值函數,第二個是賦值函數,第三個是置空專門用於下列場景
RefPtr<int> b(new int);
b = nullptr;
RefPtr還重載了指針操作符 *和-> 方便直接調用內部指針,所以其使用方法與普通指針一樣。也提供了get方法獲取到指針
inline operator T * () const { return reinterpret_cast<T*>(_ptr); }
inline T & operator * () const
{
CCASSERT(_ptr, "Attempt to dereference a null pointer!");
return reinterpret_cast<T&>(*_ptr);
}
inline T * operator->() const
{
CCASSERT(_ptr, "Attempt to dereference a null pointer!");
return reinterpret_cast<T*>(_ptr);
}
inline T * get() const { return reinterpret_cast<T*>(_ptr); }
還重載了一系列的操作符,這裏就不做分析了,最後還有一個比較關鍵的函數,weakAssign,它對保持對一個指針的弱引用,實現如下:
inline void weakAssign(const RefPtr<T> & other)
{
CC_REF_PTR_SAFE_RELEASE(_ptr);
_ptr = other._ptr;
}
相對於其他的複製函數,他少了retain操作,說明它並不保持對other的強引用,但是析構的時候我們發現,依舊會release一次,那麼這個函數會有什麼奇妙的作用呢。看下面的函數片段
void a()
{
RefPtr<Texture2D> l;
l.weakAssign(new Texture2D);
// -- doSomething
return;
}
函數中並沒有delete,但是依舊不會造成內存泄露,當然,還有一種方法也不會造成內存泄露,也就是
auto aa = new Texture2D;
aa->autorelease();
但是這一種方法的釋放時機是在一幀的結束,而智能指針的釋放時機是函數的結束,所以相較於下一種方法,智能指針會效率更高