HBufC使用中的陷阱與誤區

Symbian是爲資源高度受限的手持終端所設計的,所以應用開發中須要時刻保持這樣的警覺。Symbian默認爲應用程序創建的棧區是較小的,因此在棧中創建緩衝區時應當特別小心。

  雖然Symbian SDK中也提供了TBuf、TBufC等可用於棧的緩衝區描述符,但一般僅限用於小緩衝區。對於大緩衝區,推薦的作法是使用HBufC類在堆中創建緩衝區描述符。接觸過HBufC類的朋友一定會覺得非常奇怪,爲什麼沒有一個對應的“可修改(Modifiable)”的HBuf類呢?這個問題還真不容易解釋,引用《Symbian Explained: Effective C++ Programming for Smartphones》一書中的說法:


  通常我們都覺得應該有一個HBuf類存在,以便與TBuf類相對應。事實上,假如這個HBuf類存在,則我們必須爲其考慮長度的自動擴充以及內存管理的一系列問題。但Symbian中描述符體系旨在被設計爲高效而不負責內存管理(由程序員去考慮內存的管理),而如果有這樣一個“可修改”的HBuf類卻沒有自動擴充機制,這會讓人覺得很古怪。

 

  所以,我們不得不用一個“不可修改(Unmodifiable)”的HBufC類來作爲當緩衝區,這似乎讓人覺得越發彆扭了(其實我寧願選擇有一個長度固定的HBuf可用 :( )。那麼我們應該如何正確的使用HBufC類呢?其實,這裏面存在的陷阱和誤區恰恰是人們認爲理所當然的那一面。

(1)HBufC的分配

  標準的作法是:HBufC::NewL 或 HBufC::NewLC,但有些時候,我們還有一個更高效的選擇,例如當需要創建一個HBufC來保存另一個描述符的內容時:

void CExample::SaveDataL(const TDesC& aData)
{
iBuffer = HBufC::NewL(aData.Length()); // HBufC CExample::iBuffer
TPtr ptr(iBuffer->Des());
ptr.Copy(aData);
...
// 應當替換爲下面這一行(更高效)
iBuffer = aData.AllocL();
}

(2)HBufC用作TDesC

  按照Symbian描述符體系的慣性思維,常常有人會想到使用HBufC::Des()來達到這個目的。但這樣不僅實現過程冗餘,而且還浪費了12個字節的棧區。正確的方法其實很簡單,我們只需要一個“*”:

const TDesC& CExample::GetData(void)
{
return(*iBuffer);
}

(3)HBufC用作TDes

  你或許會問,這下我用HBufC::Des()總不會有問題了吧?方法確實沒錯,不過,最大的陷阱就在這裏!

  HBufC是一個“不可修改”的描述符,它的實際內存佈局中頭部只含有一個表示“長度”的TUint。那麼構造TDes所必需的另一個元素“最大長度”從哪裏來的呢?在這個問題的處理上,Symbian用了一點小小的技巧(編程中的trick往往都是災難的罪魁禍首……)。TDes的“最大長度”其實是從HBufC所佔據的這塊內存區間的“分配單元描述(Heap Cell Struct)”(分配內存單元時記錄分配信息的頭部結構)中獲得的,而這樣得到的“最大長度”常常是大於我們用HBufC::NewL()創建緩衝區時填寫的長度!!!這是因爲實際分配的內存大小是以某個粒度大小對齊的,如果當初創建緩衝區時的指定大小不是這個粒度的整倍數則會導致HBufC::Des()構造的描述符最大長度超出我們的預期。例如以下代碼:

TBool CExample::FindL(TDes& aData)
{
RFile file;
buffer = HBufC::NewL(KMaxBufferLength);
TPtr ptr = buffer.Des();
...
file.Read(ptr);
aData.Copy(ptr);
}

  如果傳入aData的最大長度也是KMaxBufferLength,那麼通常都沒有人會想到這個函數也會發生Panic吧?實際上,RFile::Read()按照ptr的最大長度讀取文件,那麼buffer中保存的數據很可能已經超過了KMaxBufferLength,下面再Copy到aData中時就會導致Panic - USER 11。

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