Download presentation
Presentation is loading. Please wait.
1
描述符 第一部分
2
引言 众所周知,Symbian OS 的字符串被称为描述符 描述符在Symbian OS 程序员中颇有声望 它是自描述的
描述符保存了它所表示的数据串的长度及其类型 (类型决定了描述符数据在内存中的实际存储情 况) 描述符在Symbian OS 程序员中颇有声望 重点是记住描述符的设计使其对低内存设备非常有效 使用尽量少的内存在存储字符串,并在长度和布局上对其进行描述
3
引言 描述符不同于标准C++的字符串,Java的字符串或者MFC的CString 描述符也不同于C的字符串
它们的内存分配和释放必须由程序员来管理 描述符也不同于C的字符串 它们防止缓冲区内存溢出并且不依赖NULL终结符来确定字符串的长度
4
描述符 Symbian OS 描述符的特性 了解 Symbian OS的描述符可以包括文本和二进制数据
了解描述符可以是窄(8位)的, 宽 (16bit)的或者为字符宽度无关的 (即16位, Symbian OS设置基于Unicode构建的) 了解描述符不能自动动态的扩展它们所引用的数据区,所以如果调用某个方法使得 要存储的数据大于可用空间就会导致系统错误(panic)
5
字符大小 Symbian OS 描述符类的字符宽度可以从名称中识别出来 Symbian supports UCS-2/UTF-16
被构建成支持unicode字符集,即缺省使用宽(16-bit) 字符 早期的Symbian OS (EPOC)版本直到v5,其描述符都使用8位的本地字符 描述符类的字符宽度可以从名称中识别出来 类名以8 结尾的描述符 (例如TPtr8)使用窄 (8位) 字符串 类名以16 结尾的描述符(例如TPtr16)处理16位字符串 Symbian supports UCS-2/UTF-16
6
字符大小 也有一组中性类 现在的 Symbian OS 总是使用宽字符构建 Symbian supports UCS-2/UTF-16
名字中没有数字 (例如TPtr) 中性类通过typedef定义为平台使用的字符宽度 定义中性类为了代码的兼容性,以方便在窄字符构建和宽字符构建之间进行切换 现在的 Symbian OS 总是使用宽字符构建 推荐的实践方法是使用中性描述符类,这样就不用显式的指定字符的宽度 Symbian supports UCS-2/UTF-16
7
内存管理 描述符类不会动态管理存储其数据所用的内存空间 修改方法会检查描述符的最大长度能否足够满足操作的成功
如果不够,它们不会为操作重新分配内存空间 而是产生系统错误以显示发生了溢出
8
内存管理 描述符的内容可以缩小和扩展 在调用描述符方法扩展数据前 最大可以达到描述符分配的最大长度
开发者应该进行必要的检查以确保描述符有足够的可用空间 Of course, the resulting length of the descriptor can be less than the maximum length allowed
9
描述符 Symbian OS 描述符类 了解TDesC, TDes, TBufC, TBuf, TPtrC, TPtr, RBuf 和 HBufC 描述符 类的特性 明白基本描述符类TDesC 和 TDes 实现了所有一般描述符的控制代码,而派生类 仅仅是添加跟它们类型相关的构造和赋值函数 识别TDesC和TDes类中修改方法的正确与错误的使用 意识到没有HBuf 类, 但RBuf可以被用作可修改的动态分配描述符
10
重要描述符类概念 它们将在后面介绍,但是在此之前了解以下几点是有用的 每个子类 TDesC 是所有描述符的基类
目前有六个描述符派生类 (TPtrC, TPtr, TBufC, TBuf, HBufC 和 RBuf) 每个子类 不实现自己的数据访问方法而是重载虚拟函数 为了访问虚拟函数表,在每个派生描述符对象中增加4个字节以存储虚拟指针(vptr) 存储描述符对象长度的4个字节中的4位用来标识描述符类的类型 Start getting familiar with these concepts at this stage and using descriptors won’t be such a hard learning curve later.
11
重要描述符类概念 描述符有两种基本结构 指针描述符 – 描述符拥有一个指针,该指针指向了其他某处存储字符串的地址
缓冲区描述符 – 字符串构成了描述符的一部分 Start getting familiar with these concepts at this stage and ramifications of using descriptors wouldn’t be such a hard learning curve later.
12
描述符类的继承层次 TDesC HBufC TDes TBufBase TBufCBase TPtrC TPtr
TBufC<n> TBuf<n> RBuf 继承树初探
13
Symbian OS 描述符类: TDesC TDesC HBufC TDes TBufBase TBufCBase TPtrC TPtr
TBufC<n> TBuf<n> RBuf
14
Symbian OS 描述符类: TDesC 所有描述符类从 TDesC类继承 处理文字描述符 (稍后讨论) 前缀 T表示是一种简单类型类
Constant is not strictly constant as some ‘C’ descriptors maybe indirectly modifiable e.g. TBufC<n> and HBufC
15
Symbian OS 描述符类: TDesC TDesC 定义了每个描述符类型的基本结构 为了标识每个继承类 type iLength
前面的4位被用来指示描述符内存结构的类型 剩下的28位被用来表示描述符数据的长度 描述符子最大长度是2^28 字节 (256 MB) 0..3 4..31 type iLength TDesC 4 bytes Constant is not strictly constant as some ‘C’ descriptors maybe indirectly modifiable e.g. TBufC<n> and HBufC
16
Symbian OS 描述符类: TDesC TDesC 提供方法 使用方法 Length() 和 Ptr() 派生类型
全都继承这些方法,而决不会重载,因为它们对于所有类型的描述符都是同等有效的. 结果是不论描述符的类型是什么 所有的常量操作符的控制操作都由TDesC实现 代码重用的好例子 Methods are documented in full in the Symbian Library
17
Symbian OS 继承类: TDesC 访问描述符数据 根据派生描述符类的实现(缓冲区或指针)而有所不同
当描述符操作需要存储描述符数据起始部分在内存中的正确地址时 可使用TDesC 的Ptr() 方法,该方法将查看描述符的类型(最上面的4位) 并返回起始数据的正确地址
18
Symbian OS 描述符类: TDes TDesC HBufC TDes TBufBase TBufCBase TPtrC TPtr
TBufC<n> TBuf<n> RBuf
19
Symbian OS 描述符类: TDes 可修改描述符类都是从基类TDes中派生 TDes定义一系列方法来操作可修改的字符串数据
TDesC的子类 TDes 有额外的成员变量来保存描述符当前分配的内存所能够存储的最大数据长度 TDes 类的 MaxLength()方法返回这个值- 派生类不用重载 TDes定义一系列方法来操作可修改的字符串数据 包括附加,填充和格式化字符串数据 所有操作代码由 TDes实现并被派生类所继承 TDesC type 4 bytes iLength iMaxLength TDes
20
派生描述符类 描述符的两种基本结构: 指针描述符-描述符拥有一个指针,该指针指向了其他某处存储字符串的地址
缓冲区描述符-字符串是构成了描述符的一部分
21
指针描述符: TPtrC 和 TPtr TDesC HBufC TDes TBufBase TBufCBase TPtrC TPtr
TBufC<n> TBuf<n> RBuf 12
22
指针描述符: TPtrC 和 TPtr 指针描述符的字符串数据 指针描述符 是与描述符对象本身分离的 可以存储在ROM中,堆中以及栈中
存储数据的内存不被描述符所拥有 指针描述符 不知道它们所指向的内存实际存储在什么位置 指针描述符自身通常是基于栈的 但是它们可以在堆上使用,例如作为CBase派生类的成员变量
23
指针描述符: TPtrC 和 TPtr 不可修改的指针描述符 TPtrC iLength TDesC iPtr TPtrC
4 bytes iPtr TPtrC “hello world” type field removed for clarity 指向数据的数据遵循长度字,因而该描述符对象总的大小是两个字 (8 字节) 在处理C中的字符串时,TPtrC等同于 const char* 数据可以访问但是不能修改,例如描述符中的数据是常量 TDesC 基类定义的所有非修改性操作可以通过 TPtrC类型的对象来访问
24
指针描述符: TPtrC 和 TPtr 该类也定义一系列构造函数允许TPtrC能用其他对象构造: 描述符 指向内存的指针
25
指针描述符: TPtrC 和 TPtr TPtrC 构造函数实例 _LIT(KDes, "abc ....");
从一个文字描述符构造 拷贝构造自另一个TPtrC 常量缓冲区描述符 构造自TBufC TText8 是单个(8位)字符, 等同于unsigned char 构造自一个零结尾的 的C字符串 指向其他某处初始化的内存 所表示内存的长度 _LIT(KDes, "abc ...."); TPtrC ptr(KDes); TPtrC copyPtr(ptr); TBufC<100> constBuffer(KDes); TPtrC myPtr(constBuffer); const TText8* cString = (TText8*)"abc ..."; TPtrC8 anotherPtr(cString); TUint8* memoryLocation; TInt length; ... TPtrC8 memPtr(memoryLocation,length);
26
指针描述符: TPtrC 和 TPtr 可修改的指针描述符 TPtr iLength TDesC iPtr TPtr
4 bytes iPtr TPtr “hello world” iMaxLength TDes 数据位置指针 (4 字节) 遵循TDes 最大长度(4 字节),而最大长度又遵循TDesC的长度 字 (4 字节) 描述符对象共三个字 (12 字节) TPtr 类可以访问、修改字符串和二进制数据 TDes 和 TDesC 的所有可修改和不可修改的基类操作可以通过TPtr 执行
27
指针描述符: TPtrC and TPtr 类定义构造函数 编译器 一个TPtr 对象 以允许使用指向内存地址的指针来构造TPtr 类对象
适当的设置长度和最大长度 编译器 也生成缺省的构造函数和拷贝构造函数 因为在类中显式的定义成protected 或者 private 一个TPtr 对象 可以由另一个可修改的指针描述符拷贝构造 例如通过调用不可修改的缓冲区类的Des()函数
28
指针描述符: TPtrC and TPtr TPtr 构造函数例子 _LIT(KLiteralDes1, "abc ..");
TBufC<60> buf(KLiteralDes1); TPtr ptr(buf.Des()); TInt length = ptr.Length(); TInt maxLength = ptr.MaxLength(); TUint8* memoryLocation; ... TInt len = 12; TInt maxLen = 32; TPtr8 memPtr(memoryLocation, maxLen); TPtr8 memPtr2(memoryLocation, len, maxLen); TBufC 将在后面描述,拷贝构造,可修改buf中的数据 Length=37 字符 Maximum length=60字符, 指向内存的合法指针 表示数据的长度 表示数据的最大长度 从一个指向内存的指针构造指针描述符 length=0, max=32 length=12, max=32 TBufC is in the next section TBufC::Des() returns a ptr to the internal buffer and is modifiable
29
基于栈的缓冲区描述符TBufC 和 TBuf
TDesC TPtrC TBufCBase TDes TBufC<n> HBufC TPtr RBuf TBufBase TBuf<n> 12
30
基于栈的缓冲区描述符 TBufC 和 TBuf
基于栈的缓冲区描述符可以是可修改的也可以是非修改的 字符串数据构成描述符对象的一部分 在非修改描述符中位于长度字之后 在可修改描述符中位于最大长度字之后 这些描述符对大小相对固定的小字符串是有用的 (例如基于栈的字 符串) 可以看作等同于C中的char[] 但是具有溢出检查的好处
31
基于栈的缓冲区描述符 TBufC 和 TBuf
iLength TDesC 4 bytes iBuf n*sizeof(char) TBufC<n> “hello world” 用来保存常量字符串或者二进制数据 从TBufCBase中派生 (派生于 TDesC ,主要是为了继承的方便而不能直接使用) TBufC<n> 是一个瘦模板类,它使用一个整型值来确定分配给缓冲区描述符对象的数据 区的大小
32
基于栈的缓冲区描述符 TBufC 和 TBuf
一个允许非修改缓冲区从任何其他描述符的副本中构造,或者从以零结尾的字符串构造 TBufC 可以初始创建为空,以后再填充 由于数据是不可修改的 缓冲区的所有内容可以通过类定义的赋值操作符来替换 替代的数据可以是另一个非修改的描述符或者是以零结尾的字符串 在每个例子中 新的数据长度不能超过缓冲区建立时模板参数所指定的长度
33
基于栈的缓冲区描述符 TBufC and TBuf
_LIT(KPalindrome, "aabbaa"); TBufC<50> buf1(KPalindrome); TBufC<50> buf2(buf1); ... TBufC<30> buf3((TText16*)"Never odd or even"); TBufC<50> buf4; buf4 = buf1; buf1 = buf3; buf3 = buf2; 从文字描述符构造 从buf1 构造 从零结尾的C字符串构造 构造为空length = 0 拷贝和替换 buf4 包含从buf1拷贝的数据, 改变了长度 buf1 包含拷贝字buf3的数据,改变了长度 Panic! Buf3的最大长度不足与存储 buf2 的数据
34
基于栈的缓冲区描述符 TBufC 和 TBuf
可修改的缓冲区数据 TBuf<n> 类是瘦模板类 iLength TDesC 4 bytes n*sizeof(char) TBuf<n> iBuf “hello world” iMaxLength TDes 整数值决定了缓冲区允许的最大长度 从 TBufBase 中派生, TBufBase从 TDes中派生 继承了TDes 和 TDesC中所有非修改和可修改描述符操作
35
基于栈的缓冲区描述符 TBufC 和 TBuf
TBuf<n> 定义了一系列构造函数和赋值操作符 与对应的不可修改类TBufC<n>提供的类似 _LIT(KPalindrome, "aabbaa"); TBuf<40> buf1(KPalindrome); TBuf<40> buf2(buf1); TBuf8<40> buf3((TText8*)"Do Geese see God?"); TBuf<40> buf4; ... buf4 = buf2; buf3 = (TText8*)"Murder for a jar of red rum"; 从文字描述符构造 从常量缓冲区描述符构造 从零结尾的C字符串构造 构造为空 length = 0 maximum length = 40 拷贝和替换 将buf2拷贝到buf4, 更新长度 更新自C 字符串
36
动态描述符: HBufC TDesC HBufC TDes TBufBase TBufCBase TPtrC TPtr
TBufC<n> TBuf<n> RBuf
37
动态描述符: HBufC 类的结构类似于 TBufC iLength TDesC iBuf HBuf “hello world”
4 bytes iBuf n*sizeof(char) HBuf “hello world” 基于堆的描述符可用于 表示字符串数据,其尺寸太大不能置于栈中或者在编译时还不知道大小 C中使用 malloc分配数据的地方
38
动态描述符: HBufC HBufC8 和 HBufC16 类 没有公共构造函数
导出了几个静态工厂函数NewL()用以创建堆上的描述符 这些函数遵循两阶段构造模型,并且在没有足够内存时异常退出 没有公共构造函数 所有堆缓冲区必须使用这些函数中的一个构造 TDesC::Alloc() 或者 TDesC::AllocL() 可能用到——产生已有描述符的一个HBufC类型拷贝 The width neutral version HBufC is typedef ’d to HBufC16 on Symbian OS builds for v6, v7, v8 and v9
39
动态描述符: HBufC 这些描述符是不可修改的 堆描述符 与基于栈的不可修改的缓冲区描述符类一样,该类也提供了一组赋值操作符
它们将替换缓冲区中的所有内容 类中的对象可以通过调用Des()函数生成的指针描述符TPtr在运行时进行修改 堆描述符 可以根据要求的大小动态创建 但是不能自动改变大小 缓冲区必须有足够的内存空间来实现修改操作,否则会导致系统异常(panic)
40
动态描述符: HBufC HBufC 构造函数实例 注意由heapBuf->Des()获得可修改的描述 符 ptr
_LIT(KPalindrome, "Do Geese see God?"); TBufC<20> stackBuf(KPalindrome); ... HBufC* heapBuf = HBufC::NewLC(20); TInt length = heapBuf->Length(); TPtr ptr(heapBuf->Des()); ptr = stackBuf; length = heapBuf->Length(); HBufC* heapBuf2 = stackBuf.AllocLC(); length = heapBuf2->Length(); _LIT(KPalindrome2, "Palindrome"); *heapBuf2 = KPalindrome2; CleanupStack::PopAndDestroy(2, heapBuf); 分配一个最大长度为20的堆描述符 现在length = 0 修改堆描述符 拷贝stackBuf 的内容到heapBuf length = 17 由栈缓冲区生成对缓冲区对象 拷贝并替换heapBuf2 中的数据 length = 10
41
动态描述符: RBuf TDesC HBufC TDes TBufBase TBufCBase TPtrC TPtr
TBufC<n> TBuf<n> RBuf
42
动态描述符: RBuf RBuf类的行为与 HBufC 类似 RBuf 从 TDes类中派生 要求的最大长度可以动态设定
但是维护一个指向堆中内存的指针 RBuf 从 TDes类中派生 RBuf 对象很容易修改,能传递给任何指定TDesC 或者 TDes为参数的函数 不需要为修改数据创建一个TPtr对象 这使得当动态分配的描述符以后需要修改时,使用RBuf比HBufC 更好.
43
动态描述符: RBuf RBuf 有两种内部表现形式: iLength TDesC RBuf iMaxLength TDes ‘H’ ‘E’
4 bytes TUint16* iEPtrType RBuf iMaxLength TDes HBufC16* iEBufCPtrType Union of ... ‘H’ ‘E’ ‘O’ ‘L’ HBufC Heap The construction and usage of RBuf objects is discussed in more detail a little later 像TPtr 描述符类型一样指向一个仅包含描述符数据的缓冲区 RBuf 对象创建或者接收别处已存在内存的拥有权 作为一个指向堆描述符的指针HBufC* RBuf 对象接收现有堆描述符的所有权,因此所指向的对象包含完全描述符对象
44
动态描述符: RBuf 内部联合(union)表示的处理是透明的 类没有被命名为 HBuf 它是R类
描述符的操作与TDes 和 TDesC 的基类方法相对应 类没有被命名为 HBuf 因为对象不能直接在堆上创建 它是R类 因为它管理基于堆的资源,并负责在清除时释放内存
45
描述符: 第一部分 Symbian OS 描述符的特征 Symbian OS 描述符类
46
描述符 第二部分
47
描述符 描述符类的继承层次 了解描述符类的继承层次 理解描述符类继承模型的内存有效性及其影响
48
描述符类的继承层次 TDesC HBufC TDes TBufBase TBufCBase TPtrC TPtr
TBufC<n> TBuf<n> RBuf
49
描述符类的继承层次 基类 TDesC 和 TDes 每个子类 提供了描述符操作方法 为了准确的定位数据区域,必须知道它们所操作的派生类的类型
没有实现自己的数据访问方法,而是进行虚函数重载 这将为每个派生描述符类对象增加额外的4个字节以存储用以访问虚函数表的虚指针(vptr)
50
描述符类的继承层次 描述符被设计成尽可能高效 考虑到派生类所具有的特殊性 保存C++ vptr所用的尺寸开销被认为是不受欢迎的
描述符对象的存储长度4个字节的前4位被用来指示描述符类的类型
51
描述符类的继承层次 当前有六个派生类: TPtrC, TPtr, TBufC, TBuf, HBufC 和 RBuf
每个类在构造时将标识位 iType设置成适当的值 使用4 位来标识类型限制了不同类型描述符的数目最多为2^4 (= 16) 似乎这个范围将来不需要进行很大的扩展
52
描述符类的继承层次 对于所有描述符, 访问描述符数据都是通过基类TDesC 的非虚函数 Ptr()
它使用switch 语句状态检查4位,标识描述符类型,然后返回其数据开始的正确位置. 这就要求基类TDesC知道其子类的内存结构,并将其硬编码到Ptr()中
53
描述符 使用描述符 APIs 了解描述符基类TDesC 和 TDes 不能实例化
理解描述符方法Size(), Length() 和 MaxLength() 的区别 理解描述符方法 Copy() 和 Set() 的区别并且知道如何使用正确赋值函数
54
使用描述符 APIs 描述符基类 TDesC 和 TDes TDesC 和 TDes类型的对象 描述符API函数
典型的, 派生描述符仅实现构造和拷贝赋值的特定方法. TDesC 和 TDes类型的对象 由于缺省构造函数是保护类型所以不能直接实例化 实际实例化和使用的都是派生描述符 描述符API函数 在每个SDK的Symbian OS文库中都有全面的文档以及课程例子 本讲座主要关注描述符操作的一些技巧
55
使用描述符 APIs Length()和 Size()之间的区别 Symbian OS v5u (第5版unicode版)
对于8位描述符,它们的是一样的,因为一个字符的大小就是一个字节 Symbian OS v5u (第5版unicode版) 系统字符是16位的,即每个字符占用两个字节 对于中性描述符和显式声明的宽描述符,Size() 返回的值总是 Length() 返回值的 两倍
56
MaxLength() 和长度修改函数 MaxLength()函数 SetMax()函数
TDes 的MaxLength()函数返回可修改描述符的最大长度 象 TDesC 的Length() 函数,它不能被派生类重载 SetMax()函数 设置描述符当前长度到允许的最大长度 不能通过改变描述符的最大长度从而扩展或者缩小数据区
57
MaxLength() 和长度修改函数 SetLength() 函数 Zero() 函数 可用于调整字符串长度到0和最大长度之间的任何值
设置长度为0
58
Set() 和赋值操作符 指针描述符类型 提供Set()函数使其指向不同的字符串数据
_LIT(KDes1, "Sixty zippers were quickly picked from the woven jute bag"); _LIT(KDes2, "Waltz, bad nymph, for quick jigs vex"); TPtrC alpha(KDes1); TPtrC beta(KDes2); alpha.Set(KDes2); // alpha points to the data in KDes2 beta.Set(KDes1); // beta points to the data in KDes1 Literal descriptors are described later
59
Set() 和赋值操作符 TDes 提供赋值操作符 TDes::operator =()
用于将数据拷贝到任何可修改描述符的内存中 倘若要复制的数据长度不超过描述符的最大长度,否则会引发一个致命错误(panic) 容易混淆 Set() 和 TDes::operator =() Set() 重置指针描述符指向新数据区 改变长度和最大长度成员变量 TDes::operator =() 仅复制数据到现有的描述符 修改描述符长度但是不修改最大长度
60
Set() 和操作符 =() _LIT(KLiteralDes1, "Jackdaws love ...");
TBufC<60> buf(KLiteralDes1); TPtr ptr(buf.Des()); TUint16* memoryLocation; ... TInt maxLen = 40; TPtr memPtr(memoryLocation, maxLen); memPtr = ptr; // 赋值 _LIT(KLiteralDes2, "The quick brown fox ..."); TBufC<100> buf2(KLiteralDes2); TPtr ptr2(buf2.Des()); ptr.Set(ptr2); memPtr = ptr2; 指向 buf的内容 指向内存的合法指针 ... 最大长度设置max length=40 拷贝和替换 memPtr的数据是 KLiteralDes1 (37 字节), max length=40 指向buf2的数据 替换ptr所指向的内容 ptr 指向buf2的内容, max length = 100 尝试更新 memPtr, 将发生致命错误因为 ptr2 的内容 (43 字节) 超过了 memPtr 的最大长度(40 字节) Full strings - “Jackdaws love my big sphinx of quartz” and "The quick brown fox jumps over the lazy dog" in the Primer
61
使用 Des()修改数据 非修改缓冲区描述符的内容不能直接修改除非进行全部替换 但是可能间接改变数据 当数据通过Des()返回值进行修改时
基于栈和堆的描述符TBufC 和 HBufC 提供一个可以返回可修改指针描述符的方法,该 指针指向缓冲区描述符的数据 但是可能间接改变数据 调用 Des() 获得可修改指针描述符,然后通过该指针操作数据 当数据通过Des()返回值进行修改时 指针描述符和常量缓冲区描述符的length成员变量都会更新
62
使用 Des()修改数据 注意: 所有描述符 都是8位 _LIT8(KPalindrome, "Satan, oscillate...");
从文字描述构造 数据是buf中的字符串, max length = 40 利用 ptr 替换buf的内容 致命错误! KPal2 超出了buf 的最大长度限制 (=40) _LIT8(KPalindrome, "Satan, oscillate..."); TBufC8<40> buf(KPalindrome); TPtr8 ptr(buf.Des()); ... ptr = (TText8*)"Do Geese see God?"; ASSERT(ptr.Length()==buf.Length()); _LIT8(KPal2, "Are we not drawn onward..."); ptr = KPal2; // 赋值 “Satan, oscillate my metallic sonatas” and “Are we not drawn onward, we few, drawn onward to new era?” full strings
63
描述符 描述符作为函数参数 理解无论是常量数据和可由函数修改的数据,指定描述符作为函数参数的正确方法 是使用引用
64
操作符作为函数参数 描述符基类 TDesC 和 TDes API 客户端 函数提供者 除非函数接收或者返回拥有权
为所有描述符操作提供和实现APIs 可以作为函数参数和返回值,允许描述符被传入代码而不必强制依赖特定的类型 API 客户端 Should not be constrained to using a TBuf because a particular function requires it 不应因为特定的函数需要它仅限于使用TBuf 函数提供者 应当保留对传入的描述符类型的无知 除非函数接收或者返回拥有权 不需指定描述符是基于栈的还是基于对的
65
操作符作为函数参数 当定义函数时 例外:当将基于堆的描述符的拥有权作为返回值时 基类TDesC 和TDes 应当总被用作参数或返回值
为效率计,描述符参数应当以引用的方式传递 对常量描述符使用const TDesC& 或者需要修改时使用 TDes& 例外:当将基于堆的描述符的拥有权作为返回值时 需要明确指出,这样调用者才能适当得清楚它以避免内存泄露
66
描述符 动态描述符类的正确使用 识别正确技术和方法来初始化一个HBufC堆缓冲区对象 明白和验证如何使用新描述符类RBuf
67
动态描述符类的正确使用 HBufC 构造和使用 可以被替换成一行语句
HBufC 可以通过使用TDesC 实现的重载方法Alloc()或者AllocL()从现有的描述符 中生成 实例显示怎样使用DesC::AllocL()取代低效代码 void CSampleClass::UnnecessaryCodeL(const TDesC& aDes) { iHeapBuffer = HBufC::NewL(aDes.Length()); TPtr ptr(iHeapBuffer->Des()); ptr.Copy(aDes); ... } 可以被替换成一行语句 { iHeapBuffer = aDes.AllocL(); }
68
HBufC 构造和使用 HBufC 对象 也可以用该类的静态工厂方法static NewL() 进行实例化
static IMPORT_C HBufC16* NewL(TInt aMaxLength); static IMPORT_C HBufC16* NewLC(TInt aMaxLength); 这些方法用指定的最大长度创建一个新的基于堆的缓冲区描述符 如果没有足够的内存进行分配则异常退出 NewLC 方法将成功创建的描述符对象保留在清除栈上 堆描述符为空,并且长度被设置为0
69
HBufC 构造和使用 HBufC16 对象 也可以使用非异常退出函数static New()进行实例化:
static IMPORT_C HBufC16* New(TInt aMaxLength); 该函数用指定的最大长度创建新的基于堆缓冲区描述符 如果没有可用的堆内存分配给描述符,它不会异常退出 调用者必须比较返回的指针是否是NULL以便在提领(dereference)它前来确认是否分配成功 堆描述符为空,并且长度设置为0
70
HBufC 构造和使用 NewMax 方法也设置 HBufC16 的长度为最大值 如果有足够的内存供分配:
static IMPORT_C HBufC16* NewMax(TInt aMaxLength); static IMPORT_C HBufC16* NewMaxL(TInt aMaxLength); static IMPORT_C HBufC16* NewMaxLC(TInt aMaxLength); 这些函数用指定的最大长度创建一个新的基于堆的缓冲区描述符并且设置长度到最大值 没有给描述符数据赋值 如果有足够的内存供分配: NewMax() 返回 NULL 指针 () NewMaxL() 和 NewMaxLC() 将会异常退出 NewMaxLC() 将成功分配的描述符遗留在清除栈上
71
HBufC 构造和使用 从RReadStream 内容初始化HBufC16
static IMPORT_C HBufC16* NewL(RReadStream& aStream, TInt aMaxLength); static IMPORT_C HBufC16* NewLC(RReadStream& aStream, TInt aMaxLength); 这些方法分配基于堆的缓冲区描述符并且用读入流的内容对其进行初始化 从读入流读入数据直到指定的最大长度 (或者流数据的最大长度 – 二者中最短的) 并且 分配缓冲区保存该内容 典型的用于重构以前被流操作外部化到一个写入流的描述符
72
描述符的外部化和内部化 描述符使用流外部化和内部化例子
将iHeapBuffer 内容写入到一个可写流 写入描述符的长度 写入描述符的内容 用读入流的内容实例化iHeapBuffer 读入描述符的长度 分配iHeapBuffer 在iHeapBuffer上创建一个可修改描述符 用ptr 将描述符数据读入iHeapBuffer void CSampleClass::ExternalizeL(RWriteStream& aStream) const { aStream.WriteUint32L(iHeapBuffer->Length()); aStream.WriteL(*iHeapBuffer, iHeapBuffer->Length()); } /** */ void CSomeClass::InternalizeL(RReadStream& aStream) TInt size = aStream.ReadUint32L(); iHeapBuffer = HBufC::NewL(size); TPtr ptr(iHeapBuffer->Des()); aStream.ReadL(ptr,size); streams are also covered later in the course, when the file system is discussed
73
描述符的外部化和内部化 下面的代码 显示使用流操作符作为一种更有效的选择
流操作符已经被优化来尽可能的压缩描述符的元数据,这急能提高效率也能节省空间 void CSampleClass::ExternalizeL(RWriteStream& aStream) const {// Much more efficient, no wasted storage space aStream << iHeapBuffer; } void CSampleClass::InternalizeL(RReadStream& aStream) {// KMaxLength indicates the maximum length of // data to be read from the stream iHeapBuffer = HBufC::NewL(aStream, KMaxLength);
74
RBuf 虽然有非修改堆描述符类HBufC RBuf 类首先在 Symbian OS v8.0 中引入 但没有相应地可修改类 HBuf
这可能为很多开发者所期待,以便能够使堆缓冲区与栈缓冲区类TBuf对称 RBuf 类首先在 Symbian OS v8.0 中引入 但是在Symbian OS v8.1 中才第一次有文档介绍,并在Symbian OS v9 和以后版本 的手机软件设计中广泛应用
75
RBuf 构造和使用 RBuf 对象可以按照以下方式进行实例化 RBuf也可以使用 Assign()方法拥有以前存在的内存段的拥有权
用Create(), CreateMax() 或者 CreateL()指定能存储的描述符数据的最大长度 也可以存储另一个描述符内容的拷贝对RBuf 进行实例化: RBuf myRBuf; _LIT(KHelloRBuf, "Hello RBuf!"); myRBuf.Create(KHelloRBuf()); Create() 分配一个缓冲区为RBuf所引用 如果 RBuf 以前拥有缓冲区, Create() 将不会在设置新的缓冲区引用之前清除它 必须首先调用 Close() 进行显式清除 RBuf也可以使用 Assign()方法拥有以前存在的内存段的拥有权 Assign()也会使已拥有的数据成为孤儿 (应当首先调用Close() 防止内存泄漏)
76
RBuf 构造和使用 使用 Assign() 得到拥有权 得到HBufC 的拥有权
使用与清除 获得以前分配的堆内存的所有权 HBufC* myHBufC = HBufC::NewL(20); RBuf myRBuf; myRBuf.Assign(myHBufC); ... myRBuf.Close(); TInt maxSizeOfData = 20; TUint16* pointer = static_cast<TUint16*>(User::AllocL(maxSizeOfData*2)); myRBuf.Assign(pointer, maxSizeOfData);
77
RBuf 构造和使用 RBuf 类 不管理缓冲区的长度并且在需要更多内存时会重新分配
如果在RBuf 对象上调用Append()却没有足够的可用内存——则会产生一个致命错误 从基类方法是非异常退出的事实很容易了解这一点
78
RBuf 构造和使用 RBuf 操作的内存必须由程序员管理 ReAllocL() 函数可以按如下方式使用:
myRBuf 是要重置大小的缓冲区 例如. 通过Append()操作 推入清除栈中以保证异常退出安全 扩展到newLength 从清除栈移除 myRBuf.CleanupClosePushL(); myRBuf.ReAllocL(newLength); CleanupStack::Pop ();
79
RBuf 构造和使用 使用RBuf 优于HBufC ,因为:
如果在HBufC 对象上调用ReAllocL()方法会造成堆单元的移动,并且需要更新关联 的HBufC* 和TPtr 变量 这种更新对于RBuf 对象是不需要的,因为当它的指针是内部维护的
80
RBuf 构造和使用 类没有被命名为 HBuf 而是R类 如果 RBuf 它们不像 HBufC 对象是自身是直接在堆上创建的
因为它管理基于堆的资源并负责在清除时释放内存 对其他R类来说通常是调用Close()执行清除 如果 RBuf 通过调用CleanupClosePushL() 被压入清除栈,要使用CleanupStack::PopAndDestroy() 来清楚它
81
RBuf 构造和使用 可以从现有的HBufC 创建RBuf 以前 何时使用 RBuf 或者 HBufC?
能够从HBufC 对象创建RBuf对于Symbian OS v8.1 以前的程序而言是一个很好的移植方法 以前 需要实例化HBufC, 然后使用关联对象TPtr ——通过调用堆描述符的Des() 方法创建 何时使用 RBuf 或者 HBufC? 当要求动态分配的缓冲区并且保存的数据是频繁改变的,推荐RBuf 当要求动态分配的描述符而其保存很少变化,则推荐HBufC
82
HBufC 和 RBuf 使用HBufC修改数据 使用RBuf 修改数据 代码很简单 – 方便理解和维护
HBufC* socketName = NULL; // KMaxNameLength 在其他地方定义 if(!socketName) { socketName = HBufC::NewL(KMaxNameLength); } // 创建科协的同伴TPtr TPtr socketNamePtr(socketName->Des()); message.ReadL(message.Ptr0(), socketNamePtr); RBuf socketName; ... if(socketName.Compare(KNullDesC)==0) { socketName.CreateL(KMaxNameLength); } message.ReadL(message.Ptr0(), socketName); 代码很简单 – 方便理解和维护
83
描述符 描述符使用中的常见低效用法 知道TFileName对象不应不加选择的随意使用,因为其所耗用的占空间
理解何时直接提领一个HBufC对象,何时调用Des() 获得一个可修改描述符 (TDes&)
84
描述符使用中的常见低效用法 TFileName 对象浪费栈空间 但是,由于每个字符都是16位宽
TFileName 类型被typedef为一个最大长度为256字符的可修改栈缓冲区 当调用各种文件系统函数来将文件名解析成完整路径时是很有用的——因为文件名的确切长度在编译 时是知道的 例如打印一个目录的内容 但是,由于每个字符都是16位宽 每次在栈上声明一个TFileName 对象,它就会消耗2 × 256 = 512个字节 还有描述符对象自己使用的12个字节 那已经超过0.5 KB! 一个应用程序的缺省栈大小只有8KB
85
TFileName 对象浪费栈空间 在Symbian OS中,每个进程的栈空间是有限的 缺省只有8 KB
在Windows模拟器中,如果还需要更多的栈空间,它将会自己扩展 但是在手机上并不是这样——栈溢出时将引发一个致命错误 这很难进行跟踪,因为在模拟器上不会有这种错误所以不能很容易的调试出来
86
TFileName 对象浪费堆空间 一个TFileName 对象消耗0.5KB 使用动态堆描述符类型是种良好的习惯
会消耗,并且潜在的浪费,栈空间的很大一部分 使用动态堆描述符类型是种良好的习惯 或者将TFileName对象的使用限制在C类成员上,因为它们是在堆上创建的
87
通过TDesC引用 HBufC HBufC 类从 TDesC中派生 调用堆描述符Des() 方法的一个常见错误
它将创建一个独立的指向描述符数据的TPtr 返回TDes&是没问题的 但是更加清晰有效的是直接HBufC 对象 const TDesC& CSampleClass::AccidentalComplexity() { return (iHeapBuffer->Des()); // 可以替换成更加有效的 return (*iHeapBuffer); }
88
描述符 文字描述符 知道如何操作文字描述符以及知道那些用L宏定义的已经被废弃了
明白使用_L 和_LIT 文字描述符的差别以及和使用前者的缺点
89
文字描述符 文字描述符不同于其他类型的描述符类型
它们与C中的static char[] 是等同的,可以内建到ROM中的程序二进制代码中(如 果代码是系统的一部分的话),因为它们是常量 e32def.h 定义一组宏,它们被用来定义两种不同的Symbian OS 文字描述符, _LIT 和 _
90
文字描述符 _LIT 宏 Symbian OS 优先使用_LIT 宏,因为它是更高效的类型 它的使用贯穿了本讲座,典型的如下所示:
_LIT(KFieldMarshalTait, "Field Marshal Tait"); KFieldMarshalTait 便可以被用作常量描述符- 例如写到文件中或者显示给用户
91
_LIT 宏 _LIT 宏 构建一个TLitC16类型的命名对象(KFieldMarshalTait) 到程序的二进制代码中
显式宏_LIT8 和_LIT16 的行为方式相似,除了LIT8 构建的是一个TLitC8类型的窄字符串
92
_LIT 宏 TLitC8 和 TLitC16 不是从 TDesC8 或者TDesC16中派生
但是它们和 TBufC8 或者TBufC16 有相同的二进制结构 这使得这些类型的对象能够用于任何TDesC 使用的地方 存储在程序二进制中的字符串用NULL指针结束,因为使用本地编译器的字符串来 构建它 _LIT 宏为非结束描述符(non-terminated descriptor)将长度调整到正确值
93
_LIT 宏 Symbian OS 也定义了表示空串的文字类 有三种空描述符变体,定义如下:
构建无关的(Build independent): 8位非Unicode字符串: 16位Unicode字符串: _LIT(KNULLDesC,""); _LIT8(KNULLDesC8,""); _LIT16(KNULLDesC16,"");
94
_L 宏 使用 _L 宏 现在在产品代码中已经被废弃了 可能仍然在测试代码中使用(那里内存的使用不是那么关键)
使用_L的好处是不用在使用之外单独声明,而是直接代替TPtrC User::Panic(_L("telephony.dll"), KErrNotSupported); 上面的例子, 字符串 (“telephony.dll”) 作为一个基本的零结尾的字符串被构建到程序的二进 制代码中
95
_L 宏 不像_LIT 宏构建的 TLitC 于是在代码执行时 没有初始的长度成员
这意味着存储文字的内存布局不像描述符的,例如没有length字 于是在代码执行时 _L 的每个实例会导致构建一个临时的TPtrC 其指针被设置为指向文字的第一个字节,因为它是存储在ROM中的
96
_LIT 和 _L 内存布局 不同内存布局 ROM 临时栈变量 ROM 12 Hello World!\0 iLength 12 iPtr
_LIT(KHello,”Hello World!”) TPtrC KHello(_L(”Hello World!”)) ROM 临时栈变量 ROM 12 Hello World!\0 iLength 12 iPtr Hello World!\0
97
描述符 描述符转换 了解如何使用描述符Copy() 函数或CnvUtfConverter 类将8位描述符转换成16 位描述符,以及将16位转换为8位 知道如何将文件中的数据读到8位描述符中并在填充的情况下将其转成16位的,以 及将16位转成8位 知道如何用TLex 类将描述符转换成数字, 已经用函数TDes::Num() 将数字换成 为描述符
98
描述符转换 在宽和窄描述符之间转换 Copy()方法 TDes实现一组Copy()重载函数,它们允许从下面几处直接复制数据到描述符:
另一个描述符 以空结尾的字符串 指针 Copy()方法 将数据复制到描述符并设置相应的长度 如果用来接收的描述符的最大长度比引入的数据长度小则函数会引发致命错误
99
描述符转换 Copy() 函数 可以 也可以 被重载成接受8位或者16位描述符 将窄描述符复制到窄描述符 将宽描述符复制到宽描述符
在不同宽度描述符之间进行复制 例如在进程中执行隐含转换
100
将宽描述符转化成窄描述符 TDes8实现的Copy()函数 C A T D \0 O \0 G \0 D O G
可以将输入的宽描述符复制到窄描述符 假定交替字符位0而将其剔除– 数据值不能扩展到 255 (decimal). 将窄描述符复制到数据区域的Copy() 函数是直接的数据拷贝 C A T TBuf8<3> cat(_L8("CAT")); TBuf16<3> dog(_L16("DOG")); cat.Copy(dog); D \0 O \0 G \0 在宽到窄的描述符拷贝中NULL 字符被剥除 D O G
101
窄描述符转换成宽描述符 对于类 TDes16 S M A L \0 L A R G E S M A L \0
输入的16位描述符可以直接复制到数据区域 Copy() 函数接收8位描述符时,在复制操作中在每个字符后面填充一个0 S M A L TBuf8<5> small(_L8("SMALL")); TBuf16<5> large(_L16("LARGE")); large.Copy(small); \0 L A R G E 用NULL字符来填充窄到宽转换的描述符 S M A L \0
102
Copy() 函数 Copy() 函数 构成了复制以及在 8位和16位描述符中转换的根本方法 当字符集被编码成每个字符一个字节
以及每个宽字符的后一个字节都进行简单的0填充时
103
CnvUtfConverter 类 用来执行正确的转换 CnvUtfConverter 类
16位Unicode (UCS-2) 和8位非Unicode 字符集合之间双向转换 或者在Unicode 和 UTF-7 和 UTF-8 转换集合之间转换 CnvUtfConverter 类 由 charconv.lib (头文件 utf.h) 提供 改了类提供一组静态函数,例如ConvertFromUnicodeToUtf8()和 ConvertToUnicodeFromUtf8()
104
将数目转换为描述符 描述符可以用TLex 类转换成数字 该类提供通用的词法分析,能进行句法元素的解析以及字符串到数字的转换
使用TChar 的低于依赖函数来决定每个字符是数字,字符还是符号
105
将数目转换为描述符 TLex是构建宽度中立的类 隐含的是TLex16 TLex8 和 TLex16 也可以显式的使用
除非要求使用特定的变量,中性形式是首选 TLex 可以用数据构造,或者先以空数据构造然后设置数据 构造和赋值都可以接受(作为参数) 另一个 TLex 对象 一个非修改描述符 指向字符串数据的TUint16* 或者TUint8* 指针(分别对应 TLex16 或者 TLex8)
106
将描述符转换为数字 最简单层次 当字符串只包含数值数据,可以使用TLex 的Val() 函数将描述符内容转换成整数
_LIT(KTestLex, "54321"); TLex lex(KTestLex()); TInt value = 0; TInt err = lex.Val(value); // 如果没有错误发生,value 等于 54321 Val()函数针对不同的符号整型以及有无范围检查进行了重载 也有针对无符号整型的Val() 重载函数,它们需要传入一个基数(10,16,2或者8);TRealTLex 还 提供了几个其他API方法进行操作和解析 这些在每个SDK的Symbian OS Library中都有文档介绍
107
将数目转换为描述符 描述符类 提供许多方法将数目转换成描述符
AppendNum(), AppendNumFixedWidth(),Num() 和 NumFixedWidth()的不同重 载方法将特定数目的参数转换成字符表示 或者完全取代原来的内容或者添加数据到描述符
108
将数目转换为描述符 用转换指令(conversion directives)进行格式化 每个格式化指示
TDes 的Format(), AppendFormat(),FormatList()和AppendFormatList()方 法每一个都接收一个格式化字符串参数 包含了嵌入在格式化指令的文字 以及一堆参数 每个格式化指示 使用参数列表中的一个或多个参数 并用于将数字转换成描述符数据
109
在描述符中包裹对象 平(Flat) 数据对象 T类对象
能够用包缓冲区(package buffer) TPckgBuf 类和包指针(package pointer) TPckg 及 TPckgC类存储在描述符中, 这对发送客户端-服务器请求时的线程间和进程间数据传输十分有用 T类对象 可以整个的包裹进一个描述符(”描述符化的”),这样它就能在线程或进程间以类型安全的方式 进行传递 More on inter thread and process communications in the client server lectures.
110
在描述符中包裹对象 TPckgBuf, TPckg 和TPckgC 类型 是瘦模板类
分别派生于TBuf<n>, TPtr 和 TPtrC (见e32std.h) 这些类是类型安全的并且对要包裹的类进行了模板化 More on inter thread and process communications in the client server lectures.
111
在描述符中包裹对象 有两个包裹指针类 可修改 TPckg 或者不可修改 TPckgC 指针描述符 指向了模板包裹了的类(template- packaged class)的一个已存在的实例 可以调用附加对象的方法 (后面幻灯片中的TSample theSample) 如果它被附加到一个TPckgC 中,其operator()方法将返回一个被包裹类的常量引用
112
在描述符中包裹对象 有一个包裹缓冲区类 它是可修改的 包缓冲区 TPckgBuf 创建并且存储封装在描述符中的类的一个新实例
复制的对象由包裹缓冲区所拥有 (下一个幻灯片) 它是可修改的 调用TPckgBuf对象operator()可以找后包裹的对象,然后就可以调用该类的方法 包裹缓冲区包含初始对象的副本,因此修改的是副本,初始对象并没有变化
113
TPckg,TPckgC 和 TPckgBuf 类的内存结构
TSample theSample iLength TDesC iPtr TPtr iMaxLength TDes TPtrC copy of theSample TBuf<n> TPckg<TSample> TPckgC<TSample> TPckBuf<TSample>
114
在描述符中包裹对象 封装在每种包过类型中的简单T 类 TSample 是要包裹在描述符包裹类中的类 可以在附加对象上调用的方法
包含theSample的可修改包裹 包含theSample的不可修改包裹 包含theSample副本的可修改包裹 直接调用附加对象theSample的方法 变异错误!非常量方法 调用一个常量方法是可以的 修改theSample的副本 class TSample { public: void SampleFunction(); void ConstantSampleFunction() const; private: TInt iSampleData; }; TSample theSample; TPckg<TSample> packagePtr(theSample); TPckgC<TSample> packagePtrC(theSample); TPckgBuf<TSample> packageBuf(theSample); packagePtr().SampleFunction(); packagePtrC().SampleFunction(); packagePtrC().ConstantSampleFunction(); packageBuf().SampleFunction();
115
描述符: 第二部分 描述符类的继承层次 使用描述符APIs 描述符作为函数参数 动态描述符类的正确使用 描述符使用中的常见低效用法
文字描述符 描述符转换
Similar presentations