C++内存管理
C++显式堆内存管理
性能上有一定优势,但有如下缺点:
- 野指针:指针指向的内容已经被释放,但是其他指针还可能指向它。
- 重复释放:重复释放一个已经释放的内存单元,或者释放一个野指针,都会导致C++运行时错误。
- 内存泄露:不再被使用的内存单元如果不被释放,就会一直占用内存单元。
C++11智能指针
主要有如下三种:
- unique_ptr:不能与其他智能指针共享所指对象的内存。一旦转移成功,就不能使用原来的指针,否则会导致运行时错误。
- shared_ptr:共享同一堆分配对象的内存,它在实现上采用引用指针。只有引用计数为零时,才会真正释放占有的堆内存。
- weak_ptr:指向shared_ptr指针分配的对象内存,但不拥有该内存。我们可以使用其lock成员来访问其指向内存的一个shared_ptr对象,当其所指向的内存无效时,返回空值(nullptr)。weak_ptr指针通常可以用来验证shared_ptr指针的有效性。
为什么Cocos2d-x不使用智能指针
有如下理由:
程序员每天都面对代码,他们需要更自然的内存管理方式,就像语言自身的特性一样,甚至几乎察觉不到背后的机制。
C++垃圾回收机制
不管采用哪种方式,垃圾回收机制都可以使内存管理变得更自然。
Cocos2d-x内存管理
引用计数
Cocos2d-x中的所有对象几乎都继承自Ref基类,Ref基类的主要职责就是对对象进行引用计数管理。
- 当一个对象使用new运算符分配内存时,引用计数为1。
- 调用
retain()
方法会增加其引用计数。 - 调用
release()
方法会减少其引用计数。并且在其引用计数为0时自动调用delete运算符删除对象并释放内存。
Ref的引用计数并不是线程安全的。在多线程中,我们需要通过处理互斥锁来保证线程的安全。
自动回收池(AutoreleasePool)
Cocos2d-x在每一帧结束的时候清理当前AutoreleasePool中的对象,因此可以使用autorelease()
方法来声明一个对象指针加入AutoreleasePool。
为了化简这种声明,Cocos2d-x使用静态的
create()
方法来调用加入AutoreleasePool。同时,自定义的UI元素也应该遵循这样的风格。
AutoreleasePool队列
对于一些游戏对象而言,一帧的生命周期有些长。我们需要能够自定义AutoreleasePool的生命周长。AutoreleasePool在构造函数中将自身指针添加到PoolManager的AutoreleasePool队列中,并在析构函数中从队列中移除自己。通过在函数开头定义AutoreleasePool对象,就能控制Cocos2d-x中的autorelease对象的声明周期了。
不要动态分配AutoreleasePool对象,而始终使用自动变量。
Cocos2d-x中的智能指针
Cocos2d-x 3.1 引入了智能指针RefPtr<T>
,是基于RAII实现的。
构造函数
RefPtr需要依赖Ref的引用计数来管理内存,因此所有类型T必须是Ref类型。赋值操作符
- 对于使用左值来赋值,会使得引用计数加1。
- 而使用右值来赋值,不会使得引用计数加1。而是使用了移动复制构造函数,将其对应的内存占用转移过来。不过,有一点不同的是会释放之前旧的资源的引用计数。
弱引用赋值
RefPtr通过提供一个
weakAssign()
方法来实现弱引用。
但是在析构的时候,依然会release()
一次。
有什么用呢?示例如下:void a() { RefPtr<Texture2D> l; l.weakAssign(new Texture2D); // -- doSomething return; }
在函数中并没有delete,但是依旧不会造成内存泄露。
析构函数:对_ptr进行safe delete。
重载了“*”和“->”操作符,使得其能够直接访问资源的地址。另外也可以通过
get()
方法来访问资源的地址。RefPtr也重载了
bool()
操作符,使我们可以直接判断其有效性。