Symbian编程总结-基础篇-动态缓冲区(1)-回顾HBufC

本文章由杨芹勍原创,如需转摘请注明出处。谢谢!

  当数据尺寸在编译期不固定,而在运行期有可能要扩展到很大尺寸时,动态缓冲区在保存二进制数据方面显得非常有用。我们可以使用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方法将进行以下三个步骤:

  1. 在内存中创建一个新的基于堆描述符
  2. 将旧的堆描述符的内容复制到新的堆描述符
  3. 删除旧的堆描述符

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

  1. 如果堆描述符被加入了清理栈,将堆描述符从堆中弹出
  2. 如果在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类。这几个类将为我们解决以上问题。

 

四、参考文献

  1. Symbian OS C++ 高效编程
posted @ 2008-12-01 13:30  musée  阅读(2609)  评论(4编辑  收藏  举报