Symbian中的descriptor

来源:互联网 发布:我要买微信木马软件 编辑:程序博客网 时间:2024/04/28 02:28
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 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)函数。 

原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 冰团e购扫码注册怎么办 苏宁销售火爆怎么办 菲洛城平台黑钱怎么办 货车扣12分怎么办 烟草证下来后怎么办 余款未结该怎么办 沙袋吸盘吸不住怎么办 电视购物不退货怎么办 母亲相信电视购物怎么办 中关村商城会买到假货怎么办 年休 科室主任不同意怎么办 怡成血糖仪不准怎么办 电子血压仪不准怎么办 学java没思路怎么办 安逸花评分不足怎么办 环保投诉不管用怎么办 乐购超市会员卡怎么办 易购225冻结金额怎么办 麻将机不洗牌了怎么办 麻将桌升不起来怎么办 公司宣布破产债务怎么办 一元乐购被骗怎么办 车被墙外皮砸了怎么办 win10安全中心打不开怎么办 导航出现001错误怎么办 车导航代码错误怎么办 买了假化妆品怎么办 发物流没有单号怎么办 天添网密码忘了怎么办 新华书香卡过期怎么办 车险断了一年怎么办 天添新倒闭捷信怎么办 建行diy信用卡怎么办卡 奥体 游泳卡过期怎么办 次卡过期了怎么办 该插件不受支持怎么办 信用卡申请找不到住址怎么办 中信信用卡白户怎么办 空调外铜管裸露怎么办 空调铜管露出来怎么办 小米分期后退货怎么办