【cocos2d-x 3.x 学习笔记】对象创建方式讨论

本文探讨了cocos2d-x 3.x中创建对象实例的不同方式,包括各自的优缺点,并提供了最佳实践建议,旨在帮助开发者优化游戏性能。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1. 创建对象实例的方式比较

cocos2d-x中获取对象实例的方式与一般的C++编程方式有些不同,下面就是一段代码,比较了对象的创建、初始化和销毁过程二者不同的编写方式:

一般C++对象创建代码:
Object *obj = new Object(arg1, arg2);
delete obj;
一般C++对象在其构造函数中完成初始化操作,同时new出来的对象需要自己负责释放。

cocos2d-x中对象创建代码:
Object *obj = Object::create();
cocos2d-x中通过调用类的静态函数create()返回对象的指针,new出的对象由cocos2d-x自己的内存管理机制负责释放,因此不用担心内存释放问题。create()函数一般有三个操作:
1)创建对象,构造函数中不包含复杂逻辑,仅仅只负责初始化成员变量。
2)调用对象的初始化函数,如init()、initWithXX()等,返回bool值标识初始化是否成功。
3)调用对象继承自基类Ref的函数autorelease(),交由cocos2d-x负责内存的释放。
Object *Object::create()
{
    Object *obj = new Object;
    if (obj && obj->init())
    {
         obj->autorelease();
    }
    else
    {
         delete obj;
         obj = nullptr;
    }
    return obj;
}
cocos2d-x 中提供了宏CREATE_FUNC(__TYPE__)来完成上面的create()函数定义,不过它只能创建无参数的create()函数,如果需要在create()函数中传入参数还是的自己写。

2.cocos2d-x 对象创建方式讨论

cocos2d-x 这种方式被称为“两段构建”,即将对象实例的创建过程分为两个阶段:创建和初始化。我们完全可以采用C++中的构造函数来完成这些工作,因为C++构造函数就是设计用来初始化对象数据的,例如上面的create()函数可以使用下面代码替换:
// 构造函数
Object::Object()     
{
     this->init();
     this->autorelease();
}
// 使用
Object *obj = new Object();     
上面代码,在构造函数内部掉用初始化函数,并且调用基类Ref的autorelease()函数完成内存自动管理。之后可以正常使用obj,并且不用我们去担心何时该delete对象。

这种方式符合C++一般习惯,而且也能获得cocos2d-x所提供的内存自动管理的优点,那为什么没有使用这种方式呢?答案在于我们调用init()函数可能会失败,例如资源不存在,此时init()函数会调用失败,但是在外部却无法获取init()函数的返回值,这样我们并不知道对象是否创建成功。而且在init()函数内部如果抛出了异常,会导致异常抛出之前已分配的部分资源无法得到释放。虽然可以使用try-catch异常处理机制来捕获异常,但会使程序体积增大不少。
(2014-09-18 补充:可以给Object增加一个初始化是否成功的成员变量,在构造函数中初始化成功时将该变量置为true,这样可以通过判断该变量是否为true来判断对象是否初始化成功,这样就间接的取到了构造函数中init()函数的返回值)

关于这一点的讨论可以参考下cocos2d-x创始人王哲对于二段构建的看法:
“其实我们设计二段构造时首先考虑其优势而非兼容cocos2d-iphone. 初始化时会遇到图片资源不存在等异常,而C++构造函数无返回值,只能用try-catch来处理异常,启用try-catch会使编译后二进制文件大不 少,故需要init返回bool值。Symbian, Bada SDK,objc的alloc + init也都是二阶段构造”。

3. 优缺点

一般C++创建对象的方式,直观符合一般的C++编程习惯,缺点是需要自己负责资源的释放(这一点可以使用共享指针std::shared_ptr来最大限度避免出错),而且如果无法知晓init()函数调用是否成功(这一点可以通过增加一个是否初始化成功的成员字段,作为标识来间接判断)。总的来说通过括号里介绍的两种补充方式完全是可以替代cocos2d-x中的对象创建方式的,而且更符合C++的编码习惯,不过任何系统不是想变就变的,需要考虑到兼容性问题,所以这种方式估计难以被cocos2d-x采用。

cocos2d-x创建对象的方式,其在构造函数中初始化成员变量,在初始化函数中完成对象的初始化工作,在类的静态函数create()中完成前两步并将内存交由其内部的内存管理机制管理。可能有点绕弯,但是一旦习惯用起来还是很好的。

这种方式最大的优点在于不用担心new出的对象的释放问题,所以比较适用于类继承自Ref的情况,这样可以获得cocos2d-x所带来的内存自动管理的好处。

其次,由于不是使用构造函数,而是使用create()这样的静态工厂方法来创建对象实例,因此create()函数名可以变化,如createWithXX()等,只要返回值类型相同即可。当然这个也有可能是其缺点,因为C++推崇使用函数重载来表示同一操作,创建对象的函数名多了反而会引起混乱,这一点看法因人而异。

最后,由于create()函数内部调用了init()、initWithXX()等初始化函数,所以你必须定义一个初始化函数,不然编译会报错,这样强制性提醒你初始化对象,从而避免粗心忘记。

4.最佳实践

下面是我观察cocos2d-x源码所总结的一般cocos2d-x C++类的定义的最佳实践。

类必须包含三个函数:

1)构造函数。好多人都不写这个,最好强制性要求自己写,这样你就记得要在构造函数中对类的成员变量进行初始化。虽然可以放在init()、initWithXX()等初始化函数进行成员变量的初始化,但这样有几个不好的地方:首先,你需要在每个初始化函数中都做一遍成员变量的初始化,工作量大;其次,会使初始化函数代码膨胀;最后,无法利用构造函数初始化列表提高程序效率。这里为了防止直接使用new 操作来创建对象,我将构造函数设置成protected了,强制性要求用create()静态函数创建对象实例(如果确实需要直接new对象,并且你知道你在做什么,这条可以无视)。

2)初始化函数。完成对相关的数据初始化逻辑,如图片、声音等资源的加载。这个必须有!否则创建函数中对初始化函数的调用会失败(当然你若过不调用另当别论)。最好声明为protected函数,初始化函数主要是供create()这样创建函数调用,我们一般不会直接使用,为了避免误用,最好将其“保护”起来。

3)创建函数。是一个静态函数,返回当前对象实例,内部调用了当前类的构造函数和初始化函数。 
// 类的声明
class Object : public Layer
{
public:
     static Object *create();

protected:
     Object();
     virtual bool init();

private:
     bool     _isMoving;
     Sprite  *_sprite;
};

// 构造函数
Object::Object():
     _isMoving(false),
     _sprite(nullptr)
{}

......
 
  

5. 总结

在编写自己的类时,如果类功能较多考虑继承自类 Ref,以获得内存自动管理,并使用cocos2d-x的对象创建风格,保持代码可读性(大家对cocos2d代码风格很熟悉,如果你也是用这种风格必然读起来易理解)。

如果类的功能很单一或作为特定数据结构,例如Vec2、Size这样的类,在构造函数中可以保证不会抛出异常,考虑使用一般的C++风格来创建对象实例。

参考资料

[1]子龙山人——Cocos2D-X 设计模式:二段构建模式
http://4gamers.cn/blog/2014/06/07/cocos2d-x-design-patterns-two-stage-pattern/

[2]cocos2d官网——cocos API风格说明
http://cn.cocos2d-x.org/article/index?type=cocos2d-x&url=/doc/cocos-docs-master/manual/framework/native/v3/easy-to-learn-api-style/zh.md


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值