近日在学习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:
//一个预定义的空值,暂时还不知道其作用
static const Value Null;
//枚举类,封装所有的数据类型名
enum class Type
{
/// no value is wrapped,an empty Value
NONE = 0,/// wrap byte
BYTE,/// wrap integer
INTEGER,/// wrap unsigned
UNSIGNED,/// wrap float
FLOAT,/// wrap double
DOUBLE,/// wrap bool
BOOLEAN,/// wrap string
STRING,/// wrap vector
VECTOR,/// wrap ValueMap
MAP,/// wrap ValueMapIntKey
INT_KEY_MAP
};
private:
//用共用体封装多种数据类型 保存Value的值,极大的节省了空间
union
{
unsigned char byteVal;
int intVal;
unsigned int unsignedVal;
float floatVal;
double doubleVal;
bool boolVal;
std::string* strVal;
ValueVector* vectorVal;
ValueMap* mapVal;
ValueMapIntKey* intKeyMapVal;
}_field;
//记录当前Value内保存的数据类型
Type _type;
}
总结:Value用一个枚举类保存数据的类型,用一个共用体保存数据的值(极大的节省了空间),用这两项完成对多种数据的保存。
2.2 成员函数
class CC_DLL Value
{
public:
/*************************************** 对多种类型均设置了构造函数,并用explicit设置其不可隐式转换,实现基本类型到Value类型的转换 ****************************************/
//构造函数
Value();
explicit Value(unsigned char v);
explicit Value(int v);
explicit Value(unsigned int v);
explicit Value(float v);
explicit Value(double v);
explicit Value(bool v);
explicit Value(const char* v);
explicit Value(const std::string& v);
explicit Value(const ValueVector& v);
explicit Value(ValueVector&& v);
explicit Value(const ValueMap& v);
explicit Value(ValueMap&& v);
explicit Value(const ValueMapIntKey& v);
explicit Value(ValueMapIntKey&& v);
//拷贝构造函数
Value(const Value& other);
//移动构造函数
Value(Value&& other);
//析构函数
~Value();
/*************************************** 重载各种数据类型的=,!=,==运算符,实现基本类型与Value类型的操作 ****************************************/
Value& operator= (const Value& other);
Value& operator= (Value&& other);
Value& operator= (unsigned char v);
Value& operator= (int v);
Value& operator= (unsigned int v);
Value& operator= (float v);
Value& operator= (double v);
Value& operator= (bool v);
Value& operator= (const char* v);
Value& operator= (const std::string& v);
Value& operator= (const ValueVector& v);
Value& operator= (ValueVector&& v);
Value& operator= (const ValueMap& v);
Value& operator= (ValueMap&& v);
Value& operator= (const ValueMapIntKey& v);
Value& operator= (ValueMapIntKey&& v);
bool operator!= (const Value& v);
bool operator!= (const Value& v) const;
bool operator== (const Value& v);
bool operator== (const Value& v) const;
/*************************************** 上面的构造函数,以及运算符重载都实现的是基本类型到Value类型的转换,那么下面众多asXXX函数实现了Value类型到基本类型的转换,两者结合才是真正实现了两者的互相转换 ****************************************/
unsigned char asByte() const;
int asInt() const;
unsigned int asUnsignedInt() const;
float asFloat() const;
double asDouble() const;
bool asBool() const;
std::string asString() const;
ValueVector& asValueVector();
const ValueVector& asValueVector() const;
ValueMap& asValueMap();
const ValueMap& asValueMap() const;
ValueMapIntKey& asIntKeyMap();
const ValueMapIntKey& asIntKeyMap() const;
//判断Value是否为空
//若类型为空则Value亦为空
inline bool isNull() const { return _type == Type::NONE; }
//获取Value类型
inline Type getType() const { return _type; }
//获取类的描述,作用于string,vector,map等类型
std::string getDescription() const;
private:
//用于释放Value内变量的空间,会在析构函数里调用
void clear();
//重置Value并设置类型
void reset(Type type);
};
2.3 详细分析
2.3.1构造函数
以int,string为例,其他的都大同小异
//很直观,直接进行了 类型_type和值_field两个成员变量的初始化
Value::Value(int v)
: _type(Type::INTEGER)
{
_field.intVal = v;
}
//相较于基本类型,多了一步开辟空间
Value::Value(const std::string& v)
: _type(Type::STRING)
{
_field.strVal = new (std::nothrow) std::string();
*_field.strVal = v;
}
2.3.2 析构函数
Value::~Value()
{
clear();
}
//析构函数仅调用了clear,下面说说clear函数
void Value::clear()
{
// Free memory the old value allocated
switch (_type)
{
//如果是基本类型,则直接对其值进行重置即可
case Type::BYTE:
_field.byteVal = 0;
break;
case Type::INTEGER:
_field.intVal = 0;
break;
//........省略部分内容
/******************* 如果是string等需要释放空间的对象,调用CC_SAFE_DELETE宏来进行处理。 #define CC_SAFE_DELETE(p) do { delete (p); (p) = nullptr; } while(0) 该宏的功能很简单,释放指针指向的空间,并将指针置为空 *******************/
case Type::STRING:
CC_SAFE_DELETE(_field.strVal);
break;
case Type::VECTOR:
CC_SAFE_DELETE(_field.vectorVal);
break;
//........省略部分内容
default:
break;
}
//将type重置
_type = Type::NONE;
}
2.3.3 Value转化函数asXXX
同样,这里仅以asInt()为例
int Value::asInt() const
{
//断言宏,如果条件表达式不符合,则中断程序并输出调试语句
CCASSERT(_type != Type::VECTOR && _type != Type::MAP && _type != Type::INT_KEY_MAP,"Only base type (bool,string,double,int) could be converted");
//本身就是Int,直接返回
if (_type == Type::INTEGER)
{
return _field.intVal;
}
//根据不同的类型进行转换操作
if (_type == Type::UNSIGNED)
{
CCASSERT(_field.unsignedVal < INT_MAX,"Can only convert values < INT_MAX");
return (int)_field.unsignedVal;
}
if (_type == Type::BYTE)
{
return _field.byteVal;
}
if (_type == Type::STRING)
{
return atoi(_field.strVal->c_str());
}
if (_type == Type::FLOAT)
{
return static_cast<int>(_field.floatVal);
}
if (_type == Type::DOUBLE)
{
return static_cast<int>(_field.doubleVal);
}
if (_type == Type::BOOLEAN)
{
return _field.boolVal ? 1 : 0;
}
//若进行到这一步,则表明type不是以上类型,则认为它不能与int进行转换,直接返回0
return 0;
}
2.3.4 reset重置函数
//函数功能为将value重置为其他类型
void Value::reset(Type type)
{
//如果类型相同,则操作完成
if (_type == type)
return;
//否则,先清除之前空间,然后根据不同类型对_field进行初始化
clear();
// Allocate memory for the new value
switch (type)
{
case Type::STRING:
_field.strVal = new (std::nothrow) std::string();
break;
case Type::VECTOR:
_field.vectorVal = new (std::nothrow) ValueVector();
break;
case Type::MAP:
_field.mapVal = new (std::nothrow) ValueMap();
break;
case Type::INT_KEY_MAP:
_field.intKeyMapVal = new (std::nothrow) ValueMapIntKey();
break;
default:
break;
}
//更改_type,重置操作完成
_type = type;
}
3.总结
看了源码,没有想象中的痛苦,反而带着一种享受,感慨人家的代码怎么可以写的那么好,那么条理清楚。
总结一下:Value类通过将所有数据类型都分化为 值 和类型 两部分,然后一系列操作都围绕着两部分,来实现Value与其封装类型之间的相互转换。其代码实现并不难,但此设计思想实在是秒,尤其是将值使用共用体来表示,在完成需要的同时还节省了空间。