Cocos2d-x 3.2 智能指针
Cocos2d-x 3.2:智能指针
本文转载自深入理解Cocos2d-x 3.x:内置数据结构(1)智能指针
智能指针在 C++ 11 的标准中已经存在了,分别是unique_ptr,shared_ptr,weak_ptr,其中最常用的应该是share_ptr,它采用引用计数的方 式管理内存,当引用计数为0的时候,自动释放内存,但是由于shared_ptr考虑到了线程安全,所以会存在有较大的性能损失。所以在实时游戏开发中, 往往不会用到shared_ptr。
在Cocos2d-x 3.2以及更高的版本中,Cocos2d-x提供了自己的智能指针方案——RefPtr,这套方案实际上也是模仿C++11中的shared_ptr机制 实现的,他结合了Cocos2d-x自身的引用计数来管理内存,当然为了性能,他牺牲了线程安全(Cocos2d-x的引用计数不支持线程安全)。
下面看看Cocos2d-x中智能指针的源码,首先是构造函数:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
|
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宏了,它是智能指针专用的宏,在外部是引用不到的。实现如下:
1
2
3
4
5
6
7
8
9
10
|
#define CC_REF_PTR_SAFE_RETAIN(ptr)\ \ do \ {\ if (ptr)\ {\ const_cast <Ref*>( static_cast < const Ref*>(ptr))->retain();\ }\ \ } while (0); |
核心就是retain,保持一个强引用。
下面是声明智能指针的用法:
1
2
3
4
5
6
7
8
9
10
|
//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); |
接下来看看析构函数:
1
2
3
4
|
inline ~RefPtr() { CC_REF_PTR_SAFE_RELEASE_NULL(_ptr); } |
析构函数就简单多了,只有一个,具体还是要到宏里面。
1
2
3
4
5
6
7
8
9
10
11
|
#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并且置空。
另外,也提供了移动赋值函数以及赋值函数。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
|
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 ; } |
第一个是移动赋值函数,第二个是赋值函数,第三个是置空专门用于下列场景
1
2
|
RefPtr< int > b( new int ); b = nullptr; |
RefPtr还重载了指针操作符 *和-> 方便直接调用内部指针,所以其使用方法与普通指针一样。也提供了get方法获取到指针:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
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,它对保持对一个指针的弱引用,实现如下:
1
2
3
4
5
|
inline void weakAssign( const RefPtr<T> & other) { CC_REF_PTR_SAFE_RELEASE(_ptr); _ptr = other._ptr; } |
相对于其他的复制函数,他少了retain操作,说明它并不保持对other的强引用,但是析构的时候我们发现,依旧会release一次,那么这个函数会有什么奇妙的作用呢。看下面的函数片段
1
2
3
4
5
6
7
8
|
void a() { RefPtr<Texture2D> l; l.weakAssign( new Texture2D); // -- doSomething return ; } |
函数中并没有delete,但是依旧不会造成内存泄露,当然,还有一种方法也不会造成内存泄露,也就是
1
2
|
auto aa = new Texture2D; aa->autorelease(); |
但是这一种方法的释放时机是在一帧的结束,而智能指针的释放时机是函数的结束,所以相较于下一种方法,智能指针会效率更高。