您的位置:首页 > 编程语言

Symbian基础总结 -- 回顾HBufC

2009-02-10 11:59 381 查看
 原文出处: http://www.cnblogs.com/felixYeou/archive/2008/12/01/1344880.html

 

当数据尺寸在编译期不固定,而在运行期有可能要扩展到很大尺寸时,动态缓冲区在保存二进制数据方面显得非常有用。我们可以使用C++数组保存二进制数据,然后调用类似于memcpy的函数去动态的改变数组所占用空间的大小;我们还能够使用HBufC描述符,获取其可修改的描述符向其写入数据,然后调用ReAlloc方法扩展数组。以上两点方法可行,但是不好,因为我们得自己管理内存的分配。Symbian C++考虑到了这一点,于是引入了动态缓冲区的概念。

  基于堆的缓冲描述符HBufC的前缀H显然不符合Symbian C++的命名规范(请参看Symbian编程总结-基础篇-类类型)。在这里,“H”仅表明数据是存放在堆(Heap)上的。虽然HBufC类以“C”为后缀,意思是不可修改的,但是我们可以通过HBufC::Des()方法获取其可修改的TPtr指针,对HBufC的内容进行修改。

一、堆描述符的构建

从TDesC类的栈内容构建
TDesC类有几个方法,允许将栈中的内容复制到堆中,并返回一个HBufC指针,这些方法的函数原型如下:

    IMPORT_C HBufC16 *Alloc() const;
IMPORT_C HBufC16 *AllocL() const;
IMPORT_C HBufC16 *AllocLC() const;

特别的,如果创建不成功,Alloc返回NULL,AllocL会抛出异常。
以下代码说明TDesC::Alloc的用法:

 1 LOCAL_C void HBufCFromDesLC(HBufC*& aBuf)
2 {
3 _LIT(KText, "Hello World!");
4 aBuf = KText().AllocLC();
5 }
6
7 LOCAL_C void MainL()
8 {
9 HBufC* buf;
10 HBufCFromDesLC(buf);
11 console->Write(buf->Des());
12
13 CleanupStack::PopAndDestroy(buf);
14 }

使用HBufC::New(L,LC)方法构建
通过HBufC::New(L,LC)方法创建时,需要在参数中指定所创建的内容占用堆空间的最大长度,如果接下来填充的数据长度超过了定义的长度则会抛出异常:

    IMPORT_C static HBufC16 *New(TInt aMaxLength);
IMPORT_C static HBufC16 *NewL(TInt aMaxLength);
IMPORT_C static HBufC16 *NewLC(TInt aMaxLength);

示例代码:

 1 LOCAL_C void HBufCFromNewLC(HBufC*& aBuf)
2 {
3 aBuf = HBufC::NewLC(19);
4 _LIT(KText, "Hello World My Girl!");
5
6 TPtr ptr = aBuf->Des();
7 ptr.Append(KText);
8 }
9
10 LOCAL_C void MainL()
11 {
12 HBufC* buf;
13 HBufCFromNewLC(buf);
14 console->Write(buf->Des());
15
16 CleanupStack::PopAndDestroy(buf);
17 }

按照字面理解,以上代码第3行申请了最大长度为19的堆内存,而字符串“Hello World My Girl!”的长度为20,程序能正常运行且没有抛出异常。为什么呢?SDK是这样解释的:

therefore, the resulting maximum length of the descriptor may be larger than requested.

HBufC预留的空间会比我们申请的空间大一些,如果我们将第三行代码换成aBuf = HBufC::NewLC(18);应用程序就会抛出异常。

使用HBufC::NewMax(L,LC)方法创建
HBufC::NewMax方法与HBufC::New方法类似,唯一不同的是,在使用New方法构建HBufC时,HBufC的Length为0,而使用NewMax方法,HBufC的Length会置为MaxLength。
示例代码如下:

 1 LOCAL_C void HBufCFromNewMaxLC(HBufC*& aBuf)
2 {
3 aBuf = HBufC::NewMaxLC(19);
4 _LIT(KText, "Hello World My Girl!");
5
6 TPtr ptr = aBuf->Des();
7 ptr.Copy(KText);
8 }
9
10 LOCAL_C void MainL()
11 {
12 HBufC* buf;
13 HBufCFromNewMaxLC(buf);
14 console->Write(buf->Des());
15
16 CleanupStack::PopAndDestroy(buf);
17 }

注意此处第7行使用的是Copy方法而不是Append方法。因为NewMax方法已经将Length设为19,如果再使用Append方法会使插入的数据超过最大缓冲区长度,从而抛出异常。

 

二、 堆描述符的内容扩展

  当为HBufC所分配的现有内存不够用时,需要扩展HBufC的内容,我们可以使用ReAlloc函数对HBufC占用的堆空间进行扩展。ReAlloc方法将进行以下三个步骤:

在内存中创建一个新的基于堆描述符

将旧的堆描述符的内容复制到新的堆描述符

删除旧的堆描述符

  从以上3点我们可以了解到,HBufC::ReAlloc方法会在堆中开辟另外一块内存空间,指向原来HBufC空间的指针将会被删除。所以,为了保证程序能正确运行,我们在调用ReAlloc的时候,不要忘记调用以下方法:

如果堆描述符被加入了清理栈,将堆描述符从堆中弹出

如果在ReAlloc之前使用Des方法获取了指向堆数据的指针描述符,在ReAlloc调用之后得重新获取

以下是示例代码:

 1 LOCAL_C void HBufCReAllocLC(HBufC*& aBuf)
2 {
3 aBuf = HBufC::NewLC(20);
4
5 _LIT(KText, "Hello World My Girl!");
6 _LIT(KNewLine, "/n");'
7
8 TPtr ptr(aBuf->Des());
9 ptr.Copy(KText);
10
11 CleanupStack::Pop(aBuf);
12
13 aBuf = aBuf->ReAlloc(60);
14 CleanupStack::PushL(aBuf);
15
16 ptr.Set(aBuf->Des());
17 ptr.Append(KNewLine);
18 ptr.Append(KText);
19 }
20
21 LOCAL_C void MainL()
22 {
23 HBufC* buf;
24 HBufCReAllocLC(buf);
25 console->Write(buf->Des());
26
27 CleanupStack::PopAndDestroy(buf);
28 }

  另:第16行我们使用的是TPtr::Set方法而不是TPre::operator =()赋值方法,原因是:Set()将不仅将描述符指向新的数据区域,还将描述符的长度和最大长度两个成员变量修改了。而operator =()会将数据复制过来,但是长度和最大长度这两个成员变量不会改变。

  所以在第16行如果我们只是简单的使用ptr = aBuf->Des();,ptr的长度和最大长度没有改变,都只是在调用ReAlloc之前的20,当执行到17行时,所添加的文字内容超过了描述符的最大长度,在此处会抛出异常。

 

三、小结

  我们在这一节里简单的回顾了堆描述符HBufC。HBufC也可以作为动态缓冲区来运用,但其不够灵活,在HBufC::ReAlloc调用以后,我们得重新将指向其的指针定位。在下一节里,我们将学习Symbian C++为我们封装的动态缓冲区类CBufFlat、CBufSeg和RBuf类。这几个类将为我们解决以上问题。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息