本文章由杨芹勍原创,如需转摘请注明出处。谢谢!
当数据尺寸在编译期不固定,而在运行期有可能要扩展到很大尺寸时,动态缓冲区在保存二进制数据方面显得非常有用。我们可以使用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类。这几个类将为我们解决以上问题。
四、参考文献
- Symbian OS C++ 高效编程
posted @ 2008-12-01 13:30
杨芹勍 阅读(1385)
评论(4) 编辑 收藏 网摘 所属分类:
Symbian