分享
 
 
 

Symbian中的descriptor

王朝other·作者佚名  2006-01-09
窄屏简体版  字體: |||超大  

好久没有写blog了,最近一直在symbian上开发,对symbian中的字符串进行了一点总结,格式没有编排,而且比较混乱,实在懒得整理了。

TDesC本身只包含两个成员变量,type和length。

type用于避免虚拟函数,length表示对象的长度。这样也就决定了symbian的descriptor是一个封闭的系统,即开发者无法再从系统提供的Descriptor class通过继承以扩展其功能。

主要的成员函数有:

TInt Length() const;

const TUint8 *Ptr() const; 返回一个不可修改的指针。

TDes由于涉及到修改,因此增加了一个MaxLength,返回能够容纳的最大长度。

麻烦的是,它并没有提供一个函数返回一个可写的指针。WPtr是保护的。

因此,TBuf由于从它间接派生,也有一个iMaxLength成员。

而TBufC,HBufC就没有。

这两个类都是抽象类。但是可惜的是,symbian源代码中没有将编译器自动产生的拷贝构造函数去掉。

因此下面的代码和合法的。

extern TPtr ptr;

TDesC des = ptr;

TPtrC从TDesC派生,多了一个类似const char ×的成员变量指针

同样,TPtr从TDes派生,也多了一个char *成员变量Ptr指针

注意;TPtrC和TPtr本身并不“拥有”内存,而只是指向一个已经分配好的内存。而且TPtrC/TPtr的析构函数理所当然也不free它

指向的内存了。

另外,当调用TPtr/TDes提供的函数修改内存,当然实际的内存就修改了。

另外,甚至TPtr本身的这个指针成员变量iPtr可能并不一定就指向实际内存的首地址,唯一保证得到首地址的方法就是调用

TDesC中的Ptr()函数。例如如果你通过调用TBufC::Des()得到的TPtr对象实际它的iPtr实际指向前4个字节位置(在Windows

Simulator中)。

如果TPtr是通过TBufC/HBufC的Des()函数得来,那么调用TDes里面的函数影响到iLength的时候,TBufC/HBufC中的iLength也改

变了。

如果通过构造函数得到TPtr,因为TPtr指向的内存可以是任何可写的数据,因此它只影响自己的长度。

这样,就实际上有两种类型的TPtr,一种当长度改变的时候,需要同时设置TBufC/HBufC的长度,另外一种不要。

估计是通过TDesC里面的iType来区分。

TBufC间接从TDesC派生,有一个类似const char[]的成员变量。基本上没有什么存在的必要。

毕竟我们在栈中分配内存,基本上就是为了“可写”的使用它,而TDesC没有提供任何“写”函数。

幸亏它还提供了Des()函数获得一个可写的TPtr/TDes对象.不过还是麻烦了一些。

TBuf间接从TDes派生,有一个类似char[]的成员变量。

但是它的父类TDes也没有方法返回一个可写的指针。有一个WPtr()还是Protected的。sigh!

HBufC从TDesC派生,搞不清为什么没有一个HBuf类。

提供了一个Des()函数返回一个可写的TPtr对象。

另外,由于Descriptor既可以表示字符,又可以表示binary数据。因此默认内存分配的时候并不保留一个额外的'\0'位置。

例如TBuf<3> buf; 那么这个buf的大小就确实只是3。

TLitC就是特别为string准备的,因此使用sizeof操作符,char[]中已经包含了'\0'字符。但是cast回的TDesC的长度可能仍然

是strlen

_LIT(KStr, "hello");

TDesC desc = KStr;

int len = desc.Length(); //check the length

HBufC8 * hbuf1 = HBufC8::NewLC(1);

HBufC8 * hbuf2 = HBufC8::NewLC(4);

HBufC8 * hbuf3 = HBufC8::NewLC(8);

TDes des1 = hBuf1.Des();

des1 = hBuf2.Des();

des2 = hBuf3.Des();

CleanupStack::PopAndDestroy(3); //HBufC8

TDes / TDesC 是抽象, TPtr/TPtrC是辅助,TBuf/HBufC/TBufC这些才是具体数据保存的地方。

在VC中检查symbian的内存泄漏:

F5 直到程序Panic,然后在output window中找到类似Thread panic ALLOC: f90b380,找到对应的内存地址。

重启emulator,检查该地址对应的内容。可以在退出的时候检查,也可以在启动的时候设置“Data"断点。

Symbian中的Descripor不要互相赋值。例如

TPtr ptr = buf.Ptr(0);

ptr = buf.Ptr(1); //ptr可能并没有被修改,导致不期望的行为。

这个地方需要极度留心,所有可写的Descriptor,例如TBufC,TBuf,TPtr,HBufc,symbian都重载了operator=()函数,实际

的语义并不是你可能想要的类似于赋值语句,而是将右值的内容拷贝到左值Descriptor所指的内存。

例如:

TBuf8<5> buf1(_L8("hello"));

TBufC8<5> buf2(_L8("world"));

TPtr8 ptr( (TUint8*)buf1.Ptr(), 5, 5);

ptr = buf2.Des();

最后一句,可能原来的意思是使ptr指向buf2,但是实际上变为了buffer之间的Copy赋值语句,即将buf2的内容拷贝到ptr所指

的内存中,运行以后buf1和buf2的内容都为world。

同时,TPtr的拷贝构造函数和等于asignment操作符的含义不同。

extern TPtr b;

TPtr a = b; //调用a的拷贝构造函数,a,b指向同一块buffer

TPtr a; //编译错误,没有默认构造函数。必须先分配内存

a = b; //将b中的内容copy至a所指的内存中。

symbian中的字符串基本上可以以两种眼光来看待:

const和非const,即只读和可写的。

unicode和非unicode,即8bit和16bit字符。

因此假设一个类TPtr8,则后面加C,TPtrC8表示const版本,8变为16表示unicode版本。

TPtr被在unicode定义的情况下被typedef成TPtr16,否则为TPtr8。

symbian的字符串总是使用unicode。

symbian使用descriptor来表示一段数据,该数据既可以在内存中,也可能在只读的ROM中,既可以表示字符串,也可以表示二

进制数据,因此不依赖'\0'字符,甚至根本就没有'\0'字符。

所有descriptor的基类是TDesC,该类实际上为一个抽象类,在symbian的头文件中,该对象的所有构造函数都为protected。因

此你无法创建TDesC,也永远不会(应该说你不应该)出现如下的语句: TDesc des。

但是symbian可能疏忽了,也比较程序容易出错的是,它没有屏蔽C++自动产生的拷贝构造函数。

因此下面的语句没有任何编译警告:

extern TPtrC ptr; //TPtrC是TDesC的派生类

TDesC des = ptr; //调用默认的拷贝构造函数,造成slice切割

foo(des); //调用一个函数,需要const TDesC&参数,由于数据已被切割,结果无法预料。

它主要用于函数参数传递,作为函数参数时,不是const TDesC& des,就是const TDesC× des,它表示一段只读的数据,有数

据成员iLength表示该段内存的长度。

以下是TDesC的数据成员定义

private:

unsigned int iLength:28;

unsigned int iType:4;

由此可知,Descriptor能代表的最大内存长度为0x0FFFFFFF,剩下的4bit,iType表示派生类的类型。

其中,TDesC还有一个重要的成员函数:

public:

const TUint *Ptr() const; 返回一个指向该段数据的指针。通常来说,这个应该定义成一个纯虚拟函数,由派生类实现。

symbian为了避免虚函数带来的开销,使用iType来表示派生类的具体类型。不难想像,TDesC根据iType就可以得到具体类的数

据头地址。

TDesC所有的成员函数都是const修饰,即任何函数都不会修改this指针。

TDesC实际上是一个typedef,对应unicode的版本,它定义为TDesC16,非unicode则为TDesC8,如果表示二进制数据时,可以显

示使用TDesC8。

TDes表示一段可写的数据区域,前面说了,所有的descriptor都从TDesC派生,TDes也不例外。增加了一个数据成员,

protected:

TInt iMaxLength;

表示该段区域的最大有效长度,由于TDes不是const了,因此可写。iMaxLength可以防止越界内存访问。

该类也是一个抽象类,即无法直接构造。

一个基本的原则就是,只读的Descriptor总是直接或间接地从TDesC派生,而不会从TDes派生。

可写的Descriptor总是直接或间接地从TDes派生,当然由于TDes的基类是TDesC,因此也从TDesC派生。

TPtrC表示一段只读的内存,从TDesC派生。由一个指向只读数据的指针和该段数据的长度初始化。

增加了一个成员变量: protected: const TUint *iPtr;

例如:

const char * psz = "hello";

TPtrC8 ptr(psz, 5); //ptr指向psz,长度为5。

TPtrC8 ptr(psz + 1, 2); //ptr指向 ‘e' 到 'l',长度为二。

TPtr表示一段可写的内存,从TDes派生。由一个指向可写数据的指针和该段数据的长度初始化。

增加了一个成员变量:protected: TUint *iPtr;

例子:

char psz[] = "hello";

TPtr8 ptr(psz, 5); //ptr指向psz,长度为5。

TPtr8 ptr(psz + 1, 2); //ptr指向 ‘e' 到 'l',长度为二。

TPtr并没有提供额外的函数来操纵、修改数据,都由基类TDes提供。

例如;

ptr.LowerCase(); //小写转换

TPtr和TPtrC并不真正“拥有”数据,它们只是“指向”该段数据,因此析构函数自然也不free掉这段内存。

上面的 char psz[] = "hello";在symbian中,一般使用TBuf来定义。

TBuf是一个模板类,唯一的模板参数是拥有数据的大小。简化后,定义如下:

template <TInt S> class TBuf8 : public TDes

{

protected:

TUint8 iBuf[S];

};

因此如果我们在栈中定义上面的“hello"字符串,就可以如下:

//由于TBuf没有提供适合const char×的合适的构造函数,因此我们需要将const char ×

//强制转换成const unsigned char *。sigh!

TBuf8<5> buf((const TUInt8*) "hello");

然后我们利用TDes提供的函数可以:

buf[0] = 'H'; // "Hello"

buf.LowerCase()等 // "hello"

还有一个对应的TBufC,提供只读的版本,从TDesC派生,照我看,基本上是照顾TBuf有个匹配的const class,用处不大。

nokia series 2.0 sdk里面的几十个examples,没有一个使用它。我也不想用。:)

就好像你使用下面的语句:

const char hello[];

你在栈中分配一块内存,类型为const char。除非你一定义就初始化它,否则使用const修饰只是自找麻烦。

最后一个就是在堆中分配内存的class,例如在c中我们一般如下:

char * psz = (char *)malloc(20);

free(psz);

在symbian中,提供了一个HBufC类,注意最后带C的总是直接/间接从TDesC派生,而和TDes没有任何关系。

那么启不是只能使用基类TDesC中提供的只读的成员函数?

下面是HBufC的定义:

class HBufC8 : public TDesC8

{

public:

IMPORT_C static HBufC8 *New(TInt aMaxLength);

IMPORT_C TPtr8 Des();

private:

TText8 iBuf[1];

};

注意iBuf[1]这种写法在c中经常用来定义可变大小结构。

前面提到HBufC8间接从TDesC8派生,因此只能使用TDesC提供的“只读”成员函数,幸亏它还提供了一个Des()函数返回一个TPtr8

指针,由于TPtr由TDes继承,因此我们通过TPtr就可以修改它的内容了。sigh!

期待HBuf class?对不起,symbian没有提供。到此为止了。

例子:

HBufC8 * pbuf = HBufC8::New(100); //等价于 const char * psz = (const char *)malloc(100);

delete pbuf; //等价于 free(psz);

注意上面的const char *,sigh!分配内存后返回的指针用const修饰,那我分配一块只读的内存干什么?

因此如果要干点正事,必须调用HBufC的Des()函数得到一个可写的TPtr对象。

HBufC8 * pbuf = HBufC8::New(100); //等价于 const char * psz = (const char *)malloc(100);

TPtr8 ptr = pbuf->Des(); //类似于 char * p = (char *) psz; 好了,总算可写了。

ptr[0] = 'H'; // *p = 'H'; 调用TDes中的函数

ptr.SetLength(1); // p[1] = '\0'; 调用TDes中的函数

delete pbuf; //等价于 free(psz);

不知道基于什么理由symbian定义了一个HBufC,而不是HBuf。估计是为了省一个TDes中的iMaxLength的数据成员?

不过从”const“的HBufC 调用Des() 函数转换到“非const”的TPtr,这个额外的iMaxLength,也不知道symbian是如何实现的?

^_^,原来是调用static TInt User::AllocLen(const TAny* aCell)函数。

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有