【深入瞭解cocos2d-x 3.x】內置數據結構(1)——智能指針

智能指針在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();
但是這一種方法的釋放時機是在一幀的結束,而智能指針的釋放時機是函數的結束,所以相較於下一種方法,智能指針會效率更高


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章