1.15k likes | 1.29k Views
描述符. 第一部分. 引言. 众所周知, Symbian OS 的字符串被称为描述符 它是自描述的 描述符保存了它所表示的数据串的长度及其类型 ( 类型决定了描述符数据在内存中的实际存储情况 ) 描述符在 Symbian OS 程序员中颇有声望 重点是记住描述符的设计使其对低内存设备非常有效 使用尽量少的内存在存储字符串 , 并在长度和布局上对其进行描述. 引言. 描述符不同于标准 C++ 的字符串, Java 的字符串或者 MFC 的 CString 它们的内存分配和释放必须由程序员来管理 描述符也不同于 C 的字符串
E N D
描述符 • 第一部分
引言 • 众所周知,Symbian OS 的字符串被称为描述符 • 它是自描述的 • 描述符保存了它所表示的数据串的长度及其类型 (类型决定了描述符数据在内存中的实际存储情况) • 描述符在Symbian OS 程序员中颇有声望 • 重点是记住描述符的设计使其对低内存设备非常有效 • 使用尽量少的内存在存储字符串,并在长度和布局上对其进行描述
引言 • 描述符不同于标准C++的字符串,Java的字符串或者MFC的CString • 它们的内存分配和释放必须由程序员来管理 • 描述符也不同于C的字符串 • 它们防止缓冲区内存溢出并且不依赖NULL终结符来确定字符串的长度
描述符 Symbian OS 描述符的特性 • 了解 Symbian OS的描述符可以包括文本和二进制数据 • 了解描述符可以是窄(8位)的, 宽 (16bit)的或者为字符宽度无关的 (即16位,Symbian OS设置基于Unicode构建的) • 了解描述符不能自动动态的扩展它们所引用的数据区,所以如果调用某个方法使得要存储的数据大于可用空间就会导致系统错误(panic)
字符大小 • Symbian OS • 被构建成支持unicode字符集,即缺省使用宽(16-bit) 字符 • 早期的Symbian OS(EPOC)版本直到v5,其描述符都使用8位的本地字符 • 描述符类的字符宽度可以从名称中识别出来 • 类名以8 结尾的描述符 (例如TPtr8)使用窄 (8位) 字符串 • 类名以16 结尾的描述符(例如TPtr16)处理16位字符串
字符大小 • 也有一组中性类 • 名字中没有数字 (例如TPtr) • 中性类通过typedef定义为平台使用的字符宽度 • 定义中性类为了代码的兼容性,以方便在窄字符构建和宽字符构建之间进行切换 • 现在的 Symbian OS 总是使用宽字符构建 • 推荐的实践方法是使用中性描述符类,这样就不用显式的指定字符的宽度
内存管理 • 描述符类不会动态管理存储其数据所用的内存空间 • 修改方法会检查描述符的最大长度能否足够满足操作的成功 • 如果不够,它们不会为操作重新分配内存空间 • 而是产生系统错误以显示发生了溢出
内存管理 • 描述符的内容可以缩小和扩展 • 最大可以达到描述符分配的最大长度 • 在调用描述符方法扩展数据前 • 开发者应该进行必要的检查以确保描述符有足够的可用空间
描述符 Symbian OS 描述符类 • 了解TDesC, TDes, TBufC, TBuf, TPtrC, TPtr, RBuf 和HBufC描述符类的特性 • 明白基本描述符类TDesC和 TDes实现了所有一般描述符的控制代码,而派生类仅仅是添加跟它们类型相关的构造和赋值函数 • 识别TDesC和TDes类中修改方法的正确与错误的使用 • 意识到没有HBuf类, 但RBuf可以被用作可修改的动态分配描述符
重要描述符类概念 • 它们将在后面介绍,但是在此之前了解以下几点是有用的 • TDesC是所有描述符的基类 • 目前有六个描述符派生类 (TPtrC, TPtr, TBufC, TBuf, HBufC和 RBuf) • 每个子类 • 不实现自己的数据访问方法而是重载虚拟函数 • 为了访问虚拟函数表,在每个派生描述符对象中增加4个字节以存储虚拟指针(vptr) • 存储描述符对象长度的4个字节中的4位用来标识描述符类的类型
重要描述符类概念 • 描述符有两种基本结构 • 指针描述符 –描述符拥有一个指针,该指针指向了其他某处存储字符串的地址 • 缓冲区描述符 –字符串构成了描述符的一部分
TDesC TPtrC TBufCBase TDes TBufC<n> HBufC RBuf TBufBase TPtr TBuf<n> 描述符类的继承层次 继承树初探
TDesC TPtrC TBufCBase TDes TBufC<n> HBufC RBuf TBufBase TPtr TBuf<n> Symbian OS 描述符类: TDesC
Symbian OS 描述符类: TDesC • 所有描述符类从 TDesC类继承 • 处理文字描述符 (稍后讨论) • 前缀 T表示是一种简单类型类 • 后缀 C表示类是不可修改类型的描述符,比如:常量
TDesC type iLength 0..3 4..31 4 bytes Symbian OS 描述符类: TDesC • TDesC定义了每个描述符类型的基本结构 • 为了标识每个继承类 • 前面的4位被用来指示描述符内存结构的类型 • 剩下的28位被用来表示描述符数据的长度 • 描述符子最大长度是2^28 字节 (256 MB)
Symbian OS 描述符类: TDesC • TDesC提供方法 • 决定描述符的长度 - Length() • 访问数据 - Ptr() • 使用方法 Length()和 Ptr() • 基类 TDesC可以实现所有典型的针对常量字符串对象的操作, 例如数访问,比较和查找 • 派生类型 • 全都继承这些方法,而决不会重载,因为它们对于所有类型的描述符都是同等有效的. • 结果是不论描述符的类型是什么 所有的常量操作符的控制操作都由TDesC实现 • 代码重用的好例子
Symbian OS 继承类: TDesC • 访问描述符数据 • 根据派生描述符类的实现(缓冲区或指针)而有所不同 • 当描述符操作需要存储描述符数据起始部分在内存中的正确地址时 • 可使用TDesC 的Ptr()方法,该方法将查看描述符的类型(最上面的4位) • 并返回起始数据的正确地址
TDesC TPtrC TBufCBase TDes TBufC<n> HBufC RBuf TBufBase TPtr TBuf<n> Symbian OS 描述符类: TDes
TDes TDesC type iLength iMaxLength 4 bytes 4 bytes Symbian OS 描述符类: TDes • 可修改描述符类都是从基类TDes中派生 • TDesC的子类 • TDes有额外的成员变量来保存描述符当前分配的内存所能够存储的最大数据长度 • TDes类的MaxLength()方法返回这个值- 派生类不用重载 • TDes定义一系列方法来操作可修改的字符串数据 • 包括附加,填充和格式化字符串数据 • 所有操作代码由TDes实现并被派生类所继承
派生描述符类 • 描述符的两种基本结构: • 指针描述符-描述符拥有一个指针,该指针指向了其他某处存储字符串的地址 • 缓冲区描述符-字符串是构成了描述符的一部分
TDesC TPtrC TBufCBase TDes TBufC<n> HBufC RBuf TBufBase TPtr TBuf<n> 指针描述符: TPtrC和 TPtr 12
指针描述符: TPtrC和 TPtr • 指针描述符的字符串数据 • 是与描述符对象本身分离的 • 可以存储在ROM中,堆中以及栈中 • 存储数据的内存不被描述符所拥有 • 指针描述符 • 不知道它们所指向的内存实际存储在什么位置 • 指针描述符自身通常是基于栈的 • 但是它们可以在堆上使用,例如作为CBase派生类的成员变量
TPtrC TDesC iLength iPtr “hello world” 4 bytes 4 bytes 指针描述符: TPtrC和 TPtr 不可修改的指针描述符 TPtrC • 指向数据的数据遵循长度字,因而该描述符对象总的大小是两个字 (8 字节) • 在处理C中的字符串时,TPtrC等同于const char* • 数据可以访问但是不能修改,例如描述符中的数据是常量 • TDesC基类定义的所有非修改性操作可以通过 TPtrC类型的对象来访问
指针描述符: TPtrC和 TPtr • 该类也定义一系列构造函数允许TPtrC能用其他对象构造: • 描述符 • 指向内存的指针 • 以空为结束符的C字符串
指针描述符: TPtrC和 TPtr TPtrC构造函数实例 从一个文字描述符构造 拷贝构造自另一个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; ... TPtrC8memPtr(memoryLocation,length);
TPtr TDes TDesC iLength iMaxLength iPtr “hello world” 4 bytes 4 bytes 4 bytes 指针描述符: TPtrC和 TPtr 可修改的指针描述符 TPtr • 数据位置指针 (4 字节) 遵循TDes最大长度(4 字节),而最大长度又遵循TDesC的长度字(4 字节) • 描述符对象共三个字 (12 字节) • TPtr类可以访问、修改字符串和二进制数据 • TDes 和 TDesC的所有可修改和不可修改的基类操作可以通过TPtr执行
指针描述符: TPtrC and TPtr • 类定义构造函数 • 以允许使用指向内存地址的指针来构造TPtr类对象 • 适当的设置长度和最大长度 • 编译器 • 也生成缺省的构造函数和拷贝构造函数 • 因为在类中显式的定义成protected或者 private • 一个TPtr对象 • 可以由另一个可修改的指针描述符拷贝构造 • 例如通过调用不可修改的缓冲区类的Des()函数
指针描述符: 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字符 Maximumlength=60字符, 指向内存的合法指针 表示数据的长度 表示数据的最大长度 从一个指向内存的指针构造指针描述符 length=0, max=32 length=12, max=32
基于栈的缓冲区描述符TBufC和 TBuf TDesC TPtrC TBufCBase TDes TBufC<n> HBufC RBuf TBufBase TPtr TBuf<n> 12
基于栈的缓冲区描述符 TBufC和 TBuf • 基于栈的缓冲区描述符可以是可修改的也可以是非修改的 • 字符串数据构成描述符对象的一部分 • 在非修改描述符中位于长度字之后 • 在可修改描述符中位于最大长度字之后 • 这些描述符对大小相对固定的小字符串是有用的 (例如基于栈的字符串) • 可以看作等同于C中的char[] • 但是具有溢出检查的好处
TBufC<n> TDesC iLength iBuf “hello world” 4 bytes n*sizeof(char) 基于栈的缓冲区描述符 TBufC和 TBuf TBufC是不可修改的缓冲区类 • 用来保存常量字符串或者二进制数据 • 从TBufCBase中派生(派生于 TDesC ,主要是为了继承的方便而不能直接使用) • TBufC<n>是一个瘦模板类,它使用一个整型值来确定分配给缓冲区描述符对象的数据区的大小
基于栈的缓冲区描述符 TBufC和 TBuf • TBufC有许多构造函数 • 一个允许非修改缓冲区从任何其他描述符的副本中构造,或者从以零结尾的字符串构造 • TBufC可以初始创建为空,以后再填充 • 由于数据是不可修改的 • 缓冲区的所有内容可以通过类定义的赋值操作符来替换 • 替代的数据可以是另一个非修改的描述符或者是以零结尾的字符串 • 在每个例子中 • 新的数据长度不能超过缓冲区建立时模板参数所指定的长度
基于栈的缓冲区描述符 TBufC and TBuf TBufC构造函数实例 _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的数据
iBuf TBuf<n> “hello world” 4 bytes 4 bytes n*sizeof(char) TDes TDesC iLength iMaxLength 基于栈的缓冲区描述符 TBufC和 TBuf 可修改的缓冲区数据 TBuf<n>类是瘦模板类 • 整数值决定了缓冲区允许的最大长度 • 从 TBufBase中派生, TBufBase从TDes中派生 • 继承了TDes和 TDesC中所有非修改和可修改描述符操作
基于栈的缓冲区描述符 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 字符串
TDesC TPtrC TBufCBase TDes TBufC<n> HBufC RBuf TBufBase TPtr TBuf<n> 动态描述符: HBufC
HBuf TDesC iLength iBuf “hello world” 4 bytes n*sizeof(char) 动态描述符: HBufC 类的结构类似于 TBufC • 基于堆的描述符可用于 • 表示字符串数据,其尺寸太大不能置于栈中或者在编译时还不知道大小 • C中使用malloc分配数据的地方
动态描述符: HBufC • HBufC8和 HBufC16类 • 导出了几个静态工厂函数NewL()用以创建堆上的描述符 • 这些函数遵循两阶段构造模型,并且在没有足够内存时异常退出 • 没有公共构造函数 • 所有堆缓冲区必须使用这些函数中的一个构造 • TDesC::Alloc()或者 TDesC::AllocL() • 可能用到——产生已有描述符的一个HBufC类型拷贝
动态描述符: HBufC • 这些描述符是不可修改的 • 与基于栈的不可修改的缓冲区描述符类一样,该类也提供了一组赋值操作符 • 它们将替换缓冲区中的所有内容 • 类中的对象可以通过调用Des()函数生成的指针描述符TPtr在运行时进行修改 • 堆描述符 • 可以根据要求的大小动态创建 • 但是不能自动改变大小 • 缓冲区必须有足够的内存空间来实现修改操作,否则会导致系统异常(panic)
动态描述符: HBufC _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; length = heapBuf2->Length(); CleanupStack::PopAndDestroy(2, heapBuf); • HBufC构造函数实例 • 注意由heapBuf->Des()获得可修改的描述符 ptr 分配一个最大长度为20的堆描述符 现在length = 0 修改堆描述符 拷贝stackBuf的内容到heapBuf length = 17 由栈缓冲区生成对缓冲区对象 length = 17 拷贝并替换heapBuf2中的数据 length = 10
TDesC TPtrC TBufCBase TDes TBufC<n> HBufC RBuf TBufBase TPtr TBuf<n> 动态描述符: RBuf
动态描述符: RBuf • RBuf类的行为与HBufC类似 • 要求的最大长度可以动态设定 • 实例化时,RBuf对象可以分配自己拥有的缓冲区,也可以拥有预先分配的内存或者已存在的堆描述符的所有权 • RBuf描述符通常在栈中创建 • 但是维护一个指向堆中内存的指针 • RBuf从 TDes类中派生 • RBuf对象很容易修改,能传递给任何指定TDesC或者 TDes为参数的函数 • 不需要为修改数据创建一个TPtr对象 • 这使得当动态分配的描述符以后需要修改时,使用RBuf比HBufC更好.
‘H’ ‘E’ ‘H’ ‘L’ ‘E’ ‘L’ ‘L’ ‘O’ ‘L’ ‘O’ Heap RBuf TDes Union of ... TDesC TUint16* iEPtrType iLength iMaxLength HBufC16* iEBufCPtrType 4 bytes 4 bytes 4 bytes HBufC 动态描述符: RBuf RBuf有两种内部表现形式: • 像TPtr描述符类型一样指向一个仅包含描述符数据的缓冲区 • RBuf对象创建或者接收别处已存在内存的拥有权 • 作为一个指向堆描述符的指针HBufC* • RBuf对象接收现有堆描述符的所有权,因此所指向的对象包含完全描述符对象
动态描述符: RBuf • 内部联合(union)表示的处理是透明的 • 不需要知道特定的 RBuf对象在内部是如何表示 • 描述符的操作与TDes和 TDesC的基类方法相对应 • 类没有被命名为 HBuf • 因为对象不能直接在堆上创建 • 它是R类 • 因为它管理基于堆的资源,并负责在清除时释放内存
描述符: 第一部分 • Symbian OS 描述符的特征 • Symbian OS 描述符类
描述符 • 第二部分
描述符 描述符类的继承层次 • 了解描述符类的继承层次 • 理解描述符类继承模型的内存有效性及其影响
TDesC TPtrC TBufCBase TDes TBufC<n> HBufC RBuf TBufBase TPtr TBuf<n> 描述符类的继承层次
描述符类的继承层次 • 基类 TDesC和 TDes • 提供了描述符操作方法 • 为了准确的定位数据区域,必须知道它们所操作的派生类的类型 • 每个子类 • 没有实现自己的数据访问方法,而是进行虚函数重载 • 这将为每个派生描述符类对象增加额外的4个字节以存储用以访问虚函数表的虚指针(vptr)
描述符类的继承层次 • 描述符被设计成尽可能高效 • 保存C++ vptr所用的尺寸开销被认为是不受欢迎的 • 考虑到派生类所具有的特殊性 • 描述符对象的存储长度4个字节的前4位被用来指示描述符类的类型