【四】 Symbian的容器
Symbian在设计之初,没有拥抱STL,这就要求,它需要重新制作一些轮子,容器便是其中的一个。

CArray系列容器

Symbian的设计者,非常喜欢复杂的继承结构和保罗万象的类,CArray系列的容器,就是在这种理念下的产物。CArray是顺序容器,相当于STL的vector + list,以及更多。
CArray系列容器,在继承的最底端,也就是可实例化使用的类,都采用CArrayXxxYxx的命名方式,即:CArray + 对象单元存储方式 + 对象段存储方式。所谓对象单元存储方式,就是表征容器中每一个单元数据,是如何存放的,在CArray中,主要有四种:
  • Flat,容器中的每个数据,都是等长同类的;
  • Var,容器中的每个数据长度,可以是不同的;
  • Pak,容器中的数据分成若干部分,每一部分都有一个leading-byte表示这一段的长度,形如描述符;
  • Ptr,容器存放CBase子类对象的指针数据。
每个容器,都有一个重要的参数,它是一个整数,称为Granularity,即,每一组元素的个数。组是CArray容器分配内存的单位,在Granularity范畴内,元素都按照上述四种模式进行存储,但是,Granularity总是一个有限的数,当容器中元素填满Granularity大小,就需要新增空间来存储。每次新增空间,都是Granularity个单元,每一组单元之间,有两种连接模式,一种是Fix,一种是Seg。
Symbian手记【四】 —— Symbian的容器_手记
在Symbian OS Explained中有一幅经典图片,各种Array的存储模式,一目了然,盗窃过来,如上所示。从存储上来看,Flat方式就有如数组,一个挨一个存在一起,Ptr看上去和Var一致,指针一个挨一个存在一起,指向堆中对象,但从本质上来看,Ptr的实现与Flat的底层类似,而Var则是转为指针定制。Pak的存储有些怪异,每一坨元素有一个leading-byte的个数参数,让人不由怀疑,这玩意整个就是为描述符处心积虑准备的。
而段与段之间,Fix的犹如vector,每次扩大存储容量,都需要进行内存的重新分配,适合用在定长的场景,而不是变长。而Seg,类似于List,段之间链表方式连接,如果Granularity的一个Seg对象,就彻底沦为了链表,毫无疑问,如果你需要不是的增加存储容量,Seg方式Array应该是你的最爱。。。
在接口层面,CArray支持AppendL,InsertL,Delete等数据写入的接口,也支持At,operator [] 之类的数据读取接口,还支持Sort,Find,Compress等数据查找和处理的接口。具体实现和底层的存储相关联,具体细节没看过相关资料,只能无条件信任它的实现没有如此废柴。。。

RArray系列容器

RArray家族有两个成员,RArray和RPointerArray,从Symbian的命名风格很容易想到,前者存放普通栈对象,后者存放指针。RArray系列的实现,与CArray相比,清晰纯洁多了,它们其实就是基于X *或X **的C数组的封装,接近于STL的vector模式,只不过,和STL相比,它的模板行为还是有点伪而已。
所有的Symbian书上,都会说当你需要接近vector这样的数组的时候,用RArray而不要使用CArrayFixFlat。原因是有很多的,本质上,CArray是被Symbian大而全思想脱了后腿,它的继承结构很深,每一层都要做一些额外的Check工作,降低了效率,而RArray基本就是为这个量身打造的,没有太多额外的开销,在Int,UInt等基本数据类型上,还不辞辛劳的做了模板特化,进一步提高了效率。因此,术业有专攻,性能强与CArrayFixFlat,也不是什么稀奇事情了。

其他容器

在Symbian中,还有一些容器,比如CDesCArray系列的Array,它们和CArray系列容器本是同根生,只不过换了个皮脸,针对描述符提供了额外的一些接口,可以更好的进行字符串比较等操作。而Symbian中,我并没有发现一些非线性的容器,比如map,比如hash,比如tree。如果有,那么就是我土鳖了,如果没有,其实也是我土鳖了,因为我实在琢磨不透Symbian怎么做这么个决定的。

Thin Templates

强行插播一些广告,关于Symbian的模板。众所周知,C++模板有两个特别优秀的特征:
  • 高效。编译期决定类型,保证运行期的运行效率。
  • 安全。编译期决定类型,保证运行期的安全。
但为了达到这样的效果,也是付出了成本的,那就是类型的膨胀,与运行期技术相比,程序体积增加了不少。作为一个手机操作系统,Symbian在模板的使用上,加了个pattern,企图鱼和熊掌兼得。具体来说,就是不利用模板高效的优点,仅利用模板安全的特征,用运行期的技术,减少程序体积,这层模板的皮,它们称之为Thin Templates。
从实现上来看,标准的一个模板容器的实现,可能就是:
template <class T>
class Array<T>
{
public:
        T GetData(int index);

private:
        T * data;
};
这样的话,如果有十个不同类型使用CArray,那么就会编译出十个不同的类,使得体积膨胀。于是Symbian的Thin Templates模式这么来做:
template <class T>
class Array<T> : private BaseArray
{
public:
        T GetData(int index);
};

class CBaseArray
{
protected:
        void * data;
};
 
表面上,还是一个模板类,但实际上,存储上并没有按照类型,而是统一转成了void *。用private继承,派生基类的存储,而摒弃基类的接口,重新构造基于模板参数的接口。这样一来,安全性保证了,很多错误在编译期就可以暴露出来,而由于底层实现没有用模板,节约了一部分体积开销,这种实现,类似于早期的Java模板,只是牺牲了性能优势,对于底层来说也许真的利大于弊,但对于自己的应用来说,就不一定了。因此,如果是自己做模板,考量一下应用场景,不需要拘泥所谓的Tiny Templates。。。
 
============================
下面是真的插播广告*_*,Symbian上的,有道购物助手, 现在已经发布。第一版功能还不算很丰富,有一些bug还在处理中。欢迎有S60 v3版的童鞋下载使用,努力拍砖。。。