RArray和RPointerArray和CArray
收藏 | 分类: | 查看: 274 | 评论(0)
RArray存放的是固定长度单元值的数组,(单元值最大不超过640bytes)。RPointerArray存放的是指针的数组,即每个单元是一个指针。对RArray,模板参数一般是R和T类型,对于RPointerArray,模板参数可是任意类型。
如果在局部(本地)创建RArray,则需要使用清除栈来确保关闭数组前没有内存因为函数异常退出没泄露,用CleanupCloasePushL( T ), 将数组压入清除栈中,这将确保异常退出时,在数组上调用close(),释放分配给该数组的所有内存。
如果在局部(本地)创建RPointerArray,则它的每一个数据单元需要推入清除栈。或者应该考虑将RPointerArray数组作为类的一个成员,并且在类的析构函数中调用RPointerArray::ResetAndDestroy().
RArray和RPointerArray都实现了Reset()方法,用于释放所有分配于存储元素的内存。 但对RPointerArray而言,还需要删除每个单元指针指向的空间,所以需要使用ResetAndDestroy().
CArray:
所用的CArray类型都是用缓冲器来存储它们的数据,缓冲器(从CBufBase派生而来)提供对内存区域的访问,并且某些方面类似于描述符。然而,描述符用于存储不希望过多改变其最大大小的数据对象,而缓冲器则需要再生命周期中动态增加或收缩。存在两种类型的缓冲器:平面缓冲器和片段缓冲器。
平面缓冲器:在单个堆单元中存放它们的整个数据,一旦该单元满了,则分配新的堆,将老数据和新建数据一起拷贝到新的堆空间中,(realloc)
片段缓冲器:用链表来存数据,好处,不用复制数据, 缺点:不能随机访问,需要遍历链表
粒度:指数组空间满后,每次增长的元素(单元)数量
============================================================
Symbian中用RArray类实现排序功能
时间: 2008年10月02日 来源:本站原创 作者: 焰中红叶 浏览次数: 159
RArray类属于基础容器类,并且是比较重要和常用的一个。
如果从名字来看这是一个数据类,功能貌似和 MFC的CArray,stl的vector差不多吧?如果这么想就错了,RArray是个泛型数组容器类,但是功能比CArray vector 提供的要多。
尤其是他提供的排序和查找功能,其实现方法比较诡异,对初学者来说容易造成迷惑。个人认为这个设计比较蹩脚,至于为什么会这样,我想不出,已经写信给作者咨询了,不过还没得到答复。
RArray的排序是这样的,它规定数组中每个元素可以有一个 order key,这个order key其实就是数组元素的某4个字节至于具体是哪4个字节作为order key是由key offset决定的,key offset 是构造函数的参数,是可选的,默认为0
就是说,默认情况下,数组里面每个元素的前4个字节会被用来作为排序的key。因此RArray也规定,每个元素至少要有4个字节,并且大小必须是4个倍数,否则某些函数不能用,甚至会产生异常。
比如我们声明一个 结构体 Bar 作为数组的元素,然后构建一个Bar的数组 barArray
struct Bar { TInt iId; TInt iSize; }
RArray barArray;
于是 iId就会成为order key了,因为默认情况下key offset 为0,iId就是元素的头4个字节。如果你要用isize做order key,那么只要指定key offset为4就好了。
如果需要RArray的排序功能那么 你就需要用到一系列名字中带有KeyOrder的函数比如 InsertInSignedKeyOrderL,FindInSignedKeyOrderL等等
Insert操作时,会按照插入元素的order key排序来查找合适的Insert位置。 Find时,也是比较order key,由于查找时只比较order key,所以会写出这样有点诡异的代码比如我们要查找 iId 为 100的元素的位置 Bar foo; foo.iId = 100; TInt index = barArray.InsertInSignedKeyOrderL(foo); 查找条件中的 iSize没有初值?对没有,也没必要,因为InsertInSignedKeyOrderL只比较前4个字节 iSize用不到,所以也不用给初值。
注意 函数名中的 Signed,还有与之对应的 Unsigned,这些表示比较order key的时候是否考虑符号。就是order key是 当作TInt还是TUint来比较。
如果你不用这些带有KeyOrder的系列函数,那么和一个CArray的数组基本一样。
另外 RArray有2个特化版本,RArray,RArray,可能是为了提高效率吧。
现在我明白Symbian 里为什么没有 list map 等容器类了,RArray实在是一专多能。不过我还是认为这样的设计不够优雅,也许作者有苦衷?
================================================================================================================================
Symbian手记【四】—— 容器
Tags:Symbian,移动 Posted in 技术 我抢沙发
【四】 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 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。。。