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

|