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

来源:互联网 发布:欧洲网络与信息安全局 编辑:程序博客网 时间:2024/05/20 20:22

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

      LOCAL_C void HBufCFromDesLC(HBufC *& aBuf)
      {
      _LIT(KText, " Hello World! " );
      aBuf = KText().AllocLC();
      }
      LOCAL_C void MainL()
      {
      HBufC * buf;
      HBufCFromDesLC(buf);
      console -> Write(buf -> Des());
      CleanupStack::PopAndDestroy(buf);
      }

          

      使用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);



      示例代码:

      LOCAL_C void HBufCFromNewLC(HBufC *& aBuf)
      {
      aBuf = HBufC::NewLC( );
      _LIT(KText, " Hello World My Girl! " );
      TPtr ptr = aBuf -> Des();
      ptr.Append(KText);
      }
      LOCAL_C void MainL()
      {
      HBufC * buf;
      HBufCFromNewLC(buf);
      console -> Write(buf -> Des());
      CleanupStack::PopAndDestroy(buf);
      }



      按照字面理解,以上代码第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。
      示例代码如下:

      LOCAL_C void HBufCFromNewMaxLC(HBufC *& aBuf)
      {
      aBuf = HBufC::NewMaxLC( );
      _LIT(KText, " Hello World My Girl! " );
      TPtr ptr = aBuf -> Des();
      ptr.Copy(KText);
      }
      LOCAL_C void MainL()
      {
      HBufC * buf;
      HBufCFromNewMaxLC(buf);
      console -> Write(buf -> Des());
      CleanupStack::PopAndDestroy(buf);
      }



      注意此处第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调用之后得重新获取

以下是示例代码:

LOCAL_C void HBufCReAllocLC(HBufC *& aBuf)
{
    aBuf = HBufC::NewLC( );
    _LIT(KText, " Hello World My Girl! " );
    _LIT(KNewLine, " /n " ); TPtr ptr(aBuf -> Des());
    ptr.Copy(KText);
    CleanupStack::Pop(aBuf);
    aBuf = aBuf -> ReAlloc( 60 );
    CleanupStack::PushL(aBuf);
    ptr.Set(aBuf -> Des());
    ptr.Append(KNewLine);
    ptr.Append(KText);
}

LOCAL_C void MainL()
{
    HBufC * buf;
    HBufCReAllocLC(buf);
    console -> Write(buf -> Des());
    CleanupStack::PopAndDestroy(buf);
}


  另:第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++ 高效编程


转自:http://www.cnblogs.com/felixYeou/archive/2008/12/01/1344880.html

原创粉丝点击