Symbian基礎總結 -- 回顧HBufC

 原文出處: 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方法將進行以下三個步驟:

  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類。這幾個類將爲我們解決以上問題。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章