Symbian中的descriptor

类别:编程语言 点击:0 评论:0 推荐:

好久没有写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)函数。

 

 

 

本文地址:http://com.8s8s.com/it/it24993.htm