Urho3D数据结构

DinS          Written on 2017/12/4

在正式引入引擎的显示之前,先要看看Urho3D提供的内存管理和数据结构。
不是有标准库提供的各种数据结构了吗?为什么还要看Urho3D的?对于容器类型,确实可以选择标准库而不是Urho3D自定义的,差别并不大。有些地方需要用到Urho3D自己的数据结构,只要知道大概就可以了,行为方式跟标准库也类似。但是对于另外两个东西String和SharedPtr,需要讨论讨论。

先来说String,urho3d提供的String支持UTF16和UTF8,对于国际化而言非常有用。不过由于国际化一般只是在显示时才用到,所以你在内存管理时用标准库string也无大碍。
另外Urho3D也提供了专门的国际化方式,使用外部json文件做起来更容易。
综合而言,如果没有在内存管理时处理Unicode的需求,可以不用String。

对于SharedPtr,情况则不一样,为了使用urho3d里面的类,你只能使用这个而不是标准库的shared_ptr。
为什么呢?因为你使用引擎提供的方法创建出来的对象基本上都是返回SharedPtr,所以只能用这个。而且对于使用脚本的代码,SharedPtr会做一定特殊的处理让脚本正常运行,用标准库的shared_ptr则不行。
既然必须使用SharedPtr,那么我们有必要先研究一下。

一、SharedPtr

总体来说就是智能指针,会自动释放内存。
让我们通过代码来观察SharedPtr,先自定义一个类如下:

我们直接在VS中右键项目->添加…

但是如果此时回到main并包含该头文件,会报错:

有两种解决方法。
方法一:利用cmake。每次增加新的代码文件,把文件拷贝到build源目录,比如这里:

然后使用cmake重新build,这样新添加的文件就在工程里面了。

方法二:设置VS项目属性
右键项目->属性->附加包含目录->添加代码文件所在文件夹

方法一过于繁琐,通常的用方法二即可。
但是最后发布时还是建议用cmake来一遍,防止意外。

现在继续测试SharedPtr,在Start里加入如下代码:

编译出现问题:

这说明了一个问题:SharedPtr并不能取代标准库shared_ptr。
个人推测,为了使用SharedPtr,类中需要有特定的成员函数做额外处理,Urho3D引擎提供的类都有这个,但是自定义的类没有,所以报错。

我们再来试试标准库的shared_ptr:

编译运行:

正确调用了析构函数.

结论是:对于容器和String,优先使用标准库
当需要处理unicode时,视情况使用String
对于Urho3D提供的类,使用SharedPtr
对于自定义类,使用shared_ptr

二、Variant

对于有一个东西还是需要掌握的:Variant。因一步地包括VariantMap和VariantVector ,这个使用好了有奇效。
什么是Variant?官方的定义是Variable that supports a fixed set of types.
具体来说,所有Urho3D内部出现的数据结构都可以用Variant装载。于是Variant可以说是一个很方便的类型。从Variant获取具体类型,用成员函数Get,下面有示例。
所谓VariantVector其实就是Vector<Variant>,这样在Urho3D的语境下就是个万能型vector了。

看看演示:

凡是Urho3D里定义的类型都可以放入VariantVector。
获取数据时指定类型即可,可以用成员函数获取某种特定类型,也可以使用Get<T>,效果是一样的。
当然对于Vector<String>这种,可以返回指针,使用GetStringVectorPtr()即可。

成功获取数据。通过Get也可以看出Variant支持哪些数据结构。

VariantVector相当好用,VariantMap同理。
VariantMap本质上是一个hash table,其定义是HashMap<StringHash, Variant>。我们提供一个StringHash,然后在O(1)时间内找到对应Variant。
StringHash可以理解成字符串转成的hash值,因为字符串可以随意变化所以能够应对所有需求。使用的时候直接传字符串就可以,能够自动转换。

看代码示例:

跟标准库的map差不多,也是一个pair,可以用insert插入,不过更便捷的是[],如果没有key自动建立一个新的节点。提取也类似,因为内部都是Variant所以也用Get获得具体类型。
这里演示了获取StringVector指针的方法,避免拷贝大量数据。
还有一个就是节点不存在时候的处理:

对于没有的key,如果获取value会返回空,这个空取决于value的类型。如果是double就是0.0000,如果是String就是””,以此类推。
VariantMap绝对是非常好用的数据类型,O(1)效率并且可以放置任何类型,其中还有Sort函数。

正是因为好用,许多Urho3D的类型都内置了VariantMap,方便程序员把数据直接跟对象绑定,利于OOP。
看这样一个例子:

大图点这里

在这个例子里我们把自定义的变量与具体对象直接绑定,这样的好处是不需要在外部维护一个数据结构,直接从对象获取即可。这种处理方法可以考虑经常使用。

在掌握了框架和数据结构后,可以开始研究引擎提供的功能了,见《初识子系统与场景模型》。