cocos2dx源码阅读之万能的Value

前端之家收集整理的这篇文章主要介绍了cocos2dx源码阅读之万能的Value前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。

近日在学习cocos2dx引擎的使用时,阅读了某些常用的类的源码。在此进行总结。

1.概述

版本:cocos2d-x-3.12 
语言:C++

在cocos2dx-3.x之前,存在着一些原生类型的封装类,如 CCBool,CCFloat,CCDouble,CCinteger等,来完成对原生数据类型的封装,但在3.0版本出现之后,这些都被一个名叫Value的类替代了。

cocos2d::Value是一个模板容器类,完成了对很多数据类型的封装。如原生类型int,float,char,char*,bool,以及string,map,vector等stl模板类。
Value极大的方便了cocos2dx工程里各种数据类型之间的转换。我们可以很同意的将不同数据类型转换为Value,反之亦然。

2.源码

#include <CCValue.h>
文件位置cocos2d-x-XXX/cocos/base/CCValue.h

2.1 成员变量

class CC_DLL Value
{
public@H_502_22@:
    //一个预定义的空值,暂时还不知道其作用
    static@H_502_22@ const@H_502_22@ Value Null;

   //枚举类,封装所有的数据类型名@H_502_22@
    enum@H_502_22@ class Type
    {
        ///@H_502_22@ no value is wrapped,an empty Value@H_502_22@
        NONE = 0@H_502_22@,///@H_502_22@ wrap byte@H_502_22@
        BYTE,///@H_502_22@ wrap integer@H_502_22@
        INTEGER,///@H_502_22@ wrap unsigned@H_502_22@
        UNSIGNED,///@H_502_22@ wrap float@H_502_22@
        FLOAT,///@H_502_22@ wrap double@H_502_22@
        DOUBLE,///@H_502_22@ wrap bool@H_502_22@
        BOOLEAN,///@H_502_22@ wrap string@H_502_22@
        STRING,///@H_502_22@ wrap vector@H_502_22@
        VECTOR,///@H_502_22@ wrap ValueMap@H_502_22@
        MAP,///@H_502_22@ wrap ValueMapIntKey@H_502_22@
        INT_KEY_MAP
    };

private@H_502_22@:
    //用共用体封装多种数据类型 保存Value的值,极大的节省了空间    
    union
    {
        unsigned char@H_502_22@ byteVal;
        int@H_502_22@ intVal;
        unsigned int@H_502_22@ unsignedVal;
        float@H_502_22@ floatVal;
        double@H_502_22@ doubleVal;
        bool@H_502_22@ boolVal;

        std::string@H_502_22@* strVal;
        ValueVector* vectorVal;
        ValueMap* mapVal;
        ValueMapIntKey* intKeyMapVal;
    }_field;

    //记录当前Value内保存的数据类型@H_502_22@
    Type _type;
}

总结:Value用一个枚举类保存数据的类型,用一个共用体保存数据的值(极大的节省了空间),用这两项完成对多种数据的保存。

2.2 成员函数

class@H_502_22@ CC_DLL Value
{
public@H_502_22@:

    /*************************************** 对多种类型均设置了构造函数,并用explicit设置其不可隐式转换,实现基本类型到Value类型的转换 ****************************************/@H_502_22@

    //构造函数@H_502_22@
    Value();
    explicit@H_502_22@ Value(unsigned@H_502_22@ char@H_502_22@ v);
    explicit@H_502_22@ Value(int@H_502_22@ v);
    explicit@H_502_22@ Value(unsigned@H_502_22@ int@H_502_22@ v);
    explicit@H_502_22@ Value(float@H_502_22@ v);
    explicit@H_502_22@ Value(double@H_502_22@ v);
    explicit@H_502_22@ Value(bool@H_502_22@ v);
    explicit@H_502_22@ Value(const@H_502_22@ char@H_502_22@* v);
    explicit@H_502_22@ Value(const@H_502_22@ std@H_502_22@::string@H_502_22@& v);
    explicit@H_502_22@ Value(const@H_502_22@ ValueVector& v);
    explicit@H_502_22@ Value(ValueVector&& v);
    explicit@H_502_22@ Value(const@H_502_22@ ValueMap& v);
    explicit@H_502_22@ Value(ValueMap&& v);
    explicit@H_502_22@ Value(const@H_502_22@ ValueMapIntKey& v);
    explicit@H_502_22@ Value(ValueMapIntKey&& v);

    //拷贝构造函数@H_502_22@
    Value(const@H_502_22@ Value& other);
    //移动构造函数@H_502_22@
    Value(Value&& other);
    //析构函数@H_502_22@
    ~Value();

    /*************************************** 重载各种数据类型的=,!=,==运算符,实现基本类型与Value类型的操作 ****************************************/@H_502_22@   

    Value& operator@H_502_22@= (const@H_502_22@ Value& other);
    Value& operator@H_502_22@= (Value&& other);
    Value& operator@H_502_22@= (unsigned@H_502_22@ char@H_502_22@ v);
    Value& operator@H_502_22@= (int@H_502_22@ v);
    Value& operator@H_502_22@= (unsigned@H_502_22@ int@H_502_22@ v);
    Value& operator@H_502_22@= (float@H_502_22@ v);
    Value& operator@H_502_22@= (double@H_502_22@ v);
    Value& operator@H_502_22@= (bool@H_502_22@ v);
    Value& operator@H_502_22@= (const@H_502_22@ char@H_502_22@* v);
    Value& operator@H_502_22@= (const@H_502_22@ std@H_502_22@::string@H_502_22@& v);
    Value& operator@H_502_22@= (const@H_502_22@ ValueVector& v);
    Value& operator@H_502_22@= (ValueVector&& v);
    Value& operator@H_502_22@= (const@H_502_22@ ValueMap& v);
    Value& operator@H_502_22@= (ValueMap&& v);
    Value& operator@H_502_22@= (const@H_502_22@ ValueMapIntKey& v);
    Value& operator@H_502_22@= (ValueMapIntKey&& v);
    bool@H_502_22@ operator@H_502_22@!= (const@H_502_22@ Value& v);
    bool@H_502_22@ operator@H_502_22@!= (const@H_502_22@ Value& v) const@H_502_22@;
    bool@H_502_22@ operator@H_502_22@== (const@H_502_22@ Value& v);
    bool@H_502_22@ operator@H_502_22@== (const@H_502_22@ Value& v) const@H_502_22@;

    /*************************************** 上面的构造函数,以及运算符重载都实现的是基本类型到Value类型的转换,那么下面众多asXXX函数实现了Value类型到基本类型的转换,两者结合才是真正实现了两者的互相转换 ****************************************/@H_502_22@   

    unsigned@H_502_22@ char@H_502_22@ asByte() const@H_502_22@;
    int@H_502_22@ asInt() const@H_502_22@;
    unsigned@H_502_22@ int@H_502_22@ asUnsignedInt() const@H_502_22@;
    float@H_502_22@ asFloat() const@H_502_22@;
    double@H_502_22@ asDouble() const@H_502_22@;
    bool@H_502_22@ asBool() const@H_502_22@;
    std@H_502_22@::string@H_502_22@ asString() const@H_502_22@;
    ValueVector& asValueVector();
    const@H_502_22@ ValueVector& asValueVector() const@H_502_22@;
    ValueMap& asValueMap();
    const@H_502_22@ ValueMap& asValueMap() const@H_502_22@;
    ValueMapIntKey& asIntKeyMap();
    const@H_502_22@ ValueMapIntKey& asIntKeyMap() const@H_502_22@;

    //判断Value是否为空@H_502_22@
    //若类型为空则Value亦为空@H_502_22@
    inline@H_502_22@ bool@H_502_22@ isNull() const@H_502_22@ { return@H_502_22@ _type == Type::NONE; }

    //获取Value类型@H_502_22@
    inline@H_502_22@ Type getType() const@H_502_22@ { return@H_502_22@ _type; }

    //获取类的描述,作用于string,vector,map等类型@H_502_22@
    std@H_502_22@::string@H_502_22@ getDescription() const@H_502_22@;

private@H_502_22@:

    //用于释放Value内变量的空间,会在析构函数调用@H_502_22@
    void@H_502_22@ clear();

    //重置Value并设置类型@H_502_22@
    void@H_502_22@ reset(Type type);
};

2.3 详细分析

2.3.1构造函数

以int,string为例,其他的都大同小异

//很直观,直接进行了 类型_type和值_field两个成员变量的初始化@H_502_22@
Value::Value(int v)
: _type(Type::INTEGER)@H_502_22@
{
 _field.intVal = v;@H_502_22@
}

//相较于基本类型,多了一步开辟空间@H_502_22@
Value::Value(const std::string& v)
: _type(Type::STRING)@H_502_22@
{
 _field.strVal = new (std::nothrow) std::string();@H_502_22@
 *_field.strVal = v;@H_502_22@
}

2.3.2 析构函数

Value::~Value()
{
    clear();
}
//析构函数调用了clear,下面说说clear函数@H_502_22@

void@H_502_22@ Value::clear()
{
    // Free memory the old value allocated@H_502_22@
    switch@H_502_22@ (_type)
    {
        //如果是基本类型,则直接对其值进行重置即可@H_502_22@
        case@H_502_22@ Type::BYTE:
            _field.byteVal = 0@H_502_22@;
            break@H_502_22@;
        case@H_502_22@ Type::INTEGER:
            _field.intVal = 0@H_502_22@;
            break@H_502_22@;
        //........省略部分内容@H_502_22@

        /******************* 如果是string等需要释放空间的对象,调用CC_SAFE_DELETE宏来进行处理。 #define CC_SAFE_DELETE(p) do { delete (p); (p) = nullptr; } while(0) 该宏的功能很简单,释放指针指向的空间,并将指针置为空 *******************/@H_502_22@

        case@H_502_22@ Type::STRING:
            CC_SAFE_DELETE(_field.strVal);
            break@H_502_22@;
        case@H_502_22@ Type::VECTOR:
            CC_SAFE_DELETE(_field.vectorVal);
            break@H_502_22@;
        //........省略部分内容@H_502_22@
        default@H_502_22@:
            break@H_502_22@;
    }
    //将type重置@H_502_22@
    _type = Type::NONE;
}

2.3.3 Value转化函数asXXX

同样,这里仅以asInt()为例
int@H_502_22@ Value::asInt() const@H_502_22@
{
    //断言宏,如果条件表达式不符合,则中断程序并输出调试语句@H_502_22@
    CCASSERT(_type != Type::VECTOR && _type != Type::MAP && _type != Type::INT_KEY_MAP,"Only base type (bool,string,double,int) could be converted"@H_502_22@);
    //本身就是Int,直接返回@H_502_22@
    if@H_502_22@ (_type == Type::INTEGER)
    {
        return@H_502_22@ _field.intVal;
    }

    //根据不同的类型进行转换操作@H_502_22@
    if@H_502_22@ (_type == Type::UNSIGNED)
    {
        CCASSERT(_field.unsignedVal < INT_MAX,"Can only convert values < INT_MAX"@H_502_22@);
        return@H_502_22@ (int@H_502_22@)_field.unsignedVal;
    }

    if@H_502_22@ (_type == Type::BYTE)
    {
        return@H_502_22@ _field.byteVal;
    }

    if@H_502_22@ (_type == Type::STRING)
    {
        return@H_502_22@ atoi(_field.strVal->c_str());
    }

    if@H_502_22@ (_type == Type::FLOAT)
    {
        return@H_502_22@ static_cast@H_502_22@<int@H_502_22@>(_field.floatVal);
    }

    if@H_502_22@ (_type == Type::DOUBLE)
    {
        return@H_502_22@ static_cast@H_502_22@<int@H_502_22@>(_field.doubleVal);
    }

    if@H_502_22@ (_type == Type::BOOLEAN)
    {
        return@H_502_22@ _field.boolVal ? 1@H_502_22@ : 0@H_502_22@;
    }

    //若进行到这一步,则表明type不是以上类型,则认为它不能与int进行转换,直接返回0@H_502_22@
    return@H_502_22@ 0@H_502_22@;
}

2.3.4 reset重置函数

//函数功能为将value重置为其他类型@H_502_22@
void@H_502_22@ Value::reset@H_502_22@(Type@H_502_22@ type@H_502_22@)
{
    //如果类型相同,则操作完成@H_502_22@
    if@H_502_22@ (_type ==@H_502_22@ type@H_502_22@)
        return@H_502_22@;

    //否则,先清除之前空间,然后根据不同类型对_field进行初始化@H_502_22@
    clear();

    // Allocate memory for the new value@H_502_22@
    switch (type@H_502_22@)
    {
        case@H_502_22@ Type@H_502_22@::STRING@H_502_22@:
            _field.@H_502_22@strVal =@H_502_22@ new@H_502_22@ (std::nothrow@H_502_22@) std::string@H_502_22@();
            break;
        case@H_502_22@ Type@H_502_22@::VECTOR@H_502_22@:
            _field.@H_502_22@vectorVal =@H_502_22@ new@H_502_22@ (std::nothrow@H_502_22@) ValueVector();
            break;
        case@H_502_22@ Type@H_502_22@::MAP@H_502_22@:
            _field.@H_502_22@mapVal =@H_502_22@ new@H_502_22@ (std::nothrow@H_502_22@) ValueMap();
            break;
        case@H_502_22@ Type@H_502_22@::INT_KEY_MAP@H_502_22@:
            _field.@H_502_22@intKeyMapVal =@H_502_22@ new@H_502_22@ (std::nothrow@H_502_22@) ValueMapIntKey();
            break;
        default:
            break;
    }
    //更改_type,重置操作完成@H_502_22@
    _type =@H_502_22@ type@H_502_22@;
}

3.总结

看了源码,没有想象中的痛苦,反而带着一种享受,感慨人家的代码怎么可以写的那么好,那么条理清楚。
总结一下:Value类通过将所有数据类型都分化为 值 和类型 两部分,然后一系列操作都围绕着两部分,来实现Value与其封装类型之间的相互转换。其代码实现并不难,但此设计思想实在是秒,尤其是将值使用共用体来表示,在完成需要的同时还节省了空间。

猜你在找的Cocos2d-x相关文章