




版權說明:本文檔由用戶提供并上傳,收益歸屬內容提供方,若內容存在侵權,請進行舉報或認領
文檔簡介
/:數據類型與語法作者:謝興轉載需注明出處Symbian系統已經提供了一套已經定義好的內置的數據類型。為了保證你的代碼是編譯器無關的,應當使用下面symbian系統提供的數據類型,而不要使用原生數據類型(nativetypes,這里指標準C中的int,char等)。根本類型TIntX和TUintX(其中X=8,16和32)分別用來表示8位,16位和32位的有符號和無符號的整數。一般情況下,使用TInt和TUint就可以了,除非是在考慮代碼優化或兼容性的時候,才會用到TInt8,TInt16這樣的類型。TInt或TUint類型分別對應有符號和無符號的整數。TInt64.在版本8.0之前,Symbian系統中不支持64位的算術運算,而是用兩個32位的值來實現64位的整數,在8.0版本之后,TInt64和TUInt64才被定義為longlong類型,真正使用64位的內置數據類型。TReal32和TReal64(TReal相當于TReal64)這兩個數據類型相當于單精度和雙精度的浮點數,由于浮點數的運算要比整數慢,所以一般應盡量防止使用浮點數的運算。TTextX(其中X=8或16)分別對應窄或寬的字符(注:所謂窄字符通常ASCII碼字符,而寬字符是指unicode字符集的字符)TAny*TAny*意為指向任意內容的指針,在這種意義上講,TAny相當于void,TAny*相當于TAny*。但是,在某些場合下,void標示‘空’,如:voidhello(void);這時,不要將它改寫為:TAnyhello(TAny);TBool標示布爾類型。Symbian系統提供了兩個常量:ETrue(=1)和EFalse(=0),分別表示真和假。注意:在Symbian系統中,TBool被定義為int,而ETrue和EFalse被定義為enum,所以,如果一個函數的返回值為TBool,不要用如下的代碼來比較函數的返回值:TBoolisLarger(TInta,TIntb){return(a>b)?ETrue:EFalse;}if(isLarger(4,3)==ETrue){...}//錯誤,編譯不過。if(isLarger(4,3)){...}//正確2類和對象2.1Symbian系統中的命名習慣:在Symbian系統中編寫代碼時,應當遵守種樣幾個規則:成員變量的命名以小寫字母i開頭,方法的參數以小寫字母a開頭,例如:classPernon{public: TIntiAge; voidSetAge(TIntaAge){iAge=aAge};}在symbian系統中存在幾種不同類型的類(class),不同類型的類,其特性也各不相同。有的在堆(heap)上創立,有的在棧(stack)上創立,特別的是,類的實例(instance)的去除方式也不盡相同(下面,為了方便我們把類的類別稱為型別)。型別(classtype)可以表達這些不同的特點。每個型別都有一套定義好的關于如何創立和去除實例的規則。為了容易區分型別,Symbian系統使用了一個簡單的命名規則:類名以大寫字母開頭(T,C,R或M)。作為類的設計者,你先要考慮這個類的行為,看它到底與哪種型別匹配,一旦確定了它的類型,然后你就可以專注于該類的功能。同樣,對一個類的使用者來講,如果他不熟悉這個類,但類的命名規則可以幫助他弄清你的意圖如何用平安的方式初始化、使用和銷毀一個類的對象(object)。下面,我主要討論不同型別的主要特性。T類T類的行為類似于C++中的內置類型,因此,它們以T作前綴(〞T〞代表〞Type〞)。象內置類型一樣,它們沒有析構方法(destructor),這導致的結果是:T類不能包含具有析構方法的成員變量。所以,一般情況下,T類的成員變量只能是內置類型的數據或者是其它的T類的對象。在某些的情況下T類也可以包含其它對象的指針或引用,不過,這時它們之前是“使用〞關系,而不是“擁有〞關系(也就是說,這個T類對象并不負責對成員的創立和銷毀的工作)。不能擁有外部數據的原因是因為T類沒有析構方法。正是由于沒有析構方法,T類的對象可以在棧上創立,當程序流程退出函數或產生leave(一種代碼異常)的時候,系統自動去除它。即使T類有一個析構方法,在發生異常(在Symbian系統中,異常被稱為leave)時Symbian系統也不會調用它,因為leave沒有模仿標準C++的拋出異常的做法。T類的對象也可以在堆上創立。但是,應當在調用有可能發生異常的代碼之前,將這個對象放入到去除棧(cleanupStack),在發生異常的時候,去除棧(cleanupStack)會釋放這個對象。C類這種類都是從CBase派生來的(直接或間接)。//.hfileclassCStudent:publicCBase{ public: CStudent(){ RDebug::Print(_L("iamastudent")); }; ~CStudent() { RDebug::Print(_L("please,don'tkillme!")); } voidSampleFunction(){}; private: TIntiCode; TIntiScore;};CBase有兩個特點:首先,它有一個虛的析構方法,這樣,可以通過CBase指針來刪除它的子類。代碼如下所示:CBase*pStu=newCStudent();deletepStu;結果:iamastudentplease,don'tkillme!其次,CBase類和它的子類,重載了new操作符,這使得當它在堆上創立的時候,自動初始化為0,也就是說,當它一開始被創立出來的時候,所有的成員變量都被初始化為0,所以您不必在構造方法中去做這件事情。但是,在棧上創立對象時,情況并非這樣,因為這時沒有用到new操作。這將潛在地導致堆上創立的對象和棧上創立的對象的行為不一致。因此,C類的對象一定要在堆上創立。很明顯,當一個堆上的C類對象不再被需要時,我們需要消耗它。一個C類的對象可能以兩種方式存在:其它類的指針成員變量或是一個局部的針指變量。在前一種情況下,我們可以在類的析構方法中調用delete來刪除它;后一種情況要復雜一些,在調用任何有潛在的異常(leave)的代碼之前,要把這個指針放到去除棧(cleanupstack)中,否則有可能發生內存泄露。CBase類聲明了私有的拷貝構造方法和賦值操作(=)。這是一個很好的策略,它可以用來防止客戶代碼不小心地使用了淺拷貝或賦值的方法。由于基類的拷貝構造和賦值是私有的,所以,如果您希望您的類可以能夠使用拷貝構造方法,您必須顯式地聲明和定義拷貝構造方法和賦值操作。但是,考慮到C類的特性,深拷貝可能造成發生異常(leave)的隱患,而您絕對不能讓類的構造方法(或析構方法)發生異常(我們在本教程的后面解釋原因)。所以,如果您確實需要一個拷貝的方法,那么您可以為類添加一個的可能會發生異常的方法來完成同樣的任務,例如:CloneL()或CopyL()。如果您提供的這個類是從CBase派生的,您就不必為了防止客戶代碼使用有潛在平安問題的“淺〞拷貝,而在代碼中將這些方法聲明為私有的。R類前綴“R〞在這里代表資源(Resource),通常是外部資源,例如:文件的句柄(handle)。和C類不同,Symbian系統中不存在一個對應的RBase類,所以一個R類應當有一個構造方法來將它的資源句柄置為0,說明還沒有資源和這個新建的對象關聯在一起。但是,不要在構造方法中初始化資源句柄,因為這樣有可能使構造方法產生異常。R類中常常有類如Open(),Create()或Initialize()這樣的方法,它們用來分配資源,設置句柄成員變量的值,并返回錯誤代碼或是產生異常。R類通常也有對應的Close()或Reset()類,用來釋放資源,重置句柄的值說明沒有資源和該對象關聯。使用R類時,一個常見的錯誤是忘記調用它的Close()方法(當然,該方法也可以是其它名字,但它經常被命名為Close())或是有一個析構方法釋放資源,這會引起資源的泄露。R類通常都很小,除了資源句柄沒有其它的成員變量。因為不需要。它通常也沒有析構方法,資源的釋放都是在Close()方法中進行的。大多數情況下,R類都是作為類的成員變量或局部變量存在的。只有少數情況下,在堆上創立。您必須確保,當程序發后異常的時候,資源能被正確地釋放通常是使用資源棧。如果一個R類是一個堆上的自動變量(相對于成員變量),您一但要保證資源被釋放,而且,變量本身也要被釋放。typicallybyusingtwopushcalls:CleanupClosePushL(),orasimilarfunction,toensurethattheresourceiscleanedup,andastandardCleanupStack::PushL(TAny*)whichsimplycallsUser::Free()ontheheapcell.R類的成員變量通常都很簡單,所以一般不需要深拷貝(bitwisecopy)。R類的拷貝可能會引起混亂(想象一下:如果兩個對象同時在一個資源句柄上調用Close()方法,或兩個對象都沒有釋放資源,會發生什么情況?)如果,您想阻止任何對R類的拷貝,您應當聲明(但不定義)一個私有的構造方法和賦值操作。M類當提到多繼承的時候,它意味著從一個主要的類派生,同時也混雜基它類的功能。前綴M是單詞Mixin的首字母。Symbian系統不贊成多繼承的做法,因為這個引入額外的復雜性,M類是一個抽象類,它的作用相當于java中的接口(interface)。在Symbian系統中,M類常被用來定義回調接口或者是觀察者(observer)類。M類也可以被其它類繼承。下面我們給出兩個例子。classMAnimal{ public: virtualvoidEatL()=0;};classMDomesticAnimal:publicMAnimal{ public: virtualvoidNameL()=0;};classCCat:publicCBase,publicMDomesticAnimal{ public: virtualvoidEatL(){};//從MAnimal,經過MDomesticAnimal繼承 virtualvoidNameL(){};//從MDomesticAnimal繼承 //Otherfunctionsomittedforclarity};上面的例子演示了一個從CBase類和一個M類派生的具體類。而類MDomesticAnimal又是從MAnimal派生的。象接口一樣,由于不能被實例化,M類只能有虛(virtual)函數,不能有成員變量和構造方法。但它可以有析構方法,條件是,實現它的具體類必須是從CBase派生的。在定義完類以后,然后可以用使用它。代碼如下:CCat*cat1=newCCat;deletecat1;//正確然下面的代碼卻是錯誤的。MAnimal*cat2=newCCat;deletecat1;//錯誤當用M類的指針引用一個對象的時候,如果用delete刪除這個指針,則這個M類必須提供一個虛擬的析構方法,否則會出現系統異常(paniccode42)。將MAnimal的代碼改寫,則上面代碼沒有問題。classMAnimal{ public: virtualvoidEatL()=0; virtual~MAnimal();//增加一個虛的析構方法。};3描述符(descriptor)在Symbian系統中,字符串被稱為“描述符〞(descriptor),因為它們是自我描述的。在描述符中保存了它所表示的字符串的長度和它的底層的內存布局的信息。描述符比標準C中的字符數組和字符指針要復雜,您可能需要多花些時間來學習和掌握它的用法。關鍵是,它們的特殊設計使得它們在少量內存的設備上非常有效率,僅用非常少的內存就可以保存自己的長度和內存布局的信息。現在,讓我們來深入了解描述符的設計思想。在Symbian系統中,描述符是相當讓人迷惑的,因為它的種類繁多。不同種類的描述符具有不同的特性和用法,但又經常能相互轉換。它們不同于標準C++中的string,java語言中的string類或MFC中的CString,因為程序員必須自己管理底層的內存分配和去除工作。它們具有防治內存溢出的機制,并且不依賴NULL終結符號來決定字符串的長度,從這方而來講,它也不同于C語言中的字符串。現在我們來討論:什么是描述符?它們是如何工作的?在探討這些不同的描述符之前,先讓我們需要弄清楚一個根本的概念:什么是字符串數據的“寬度〞?這個長度指的是單個字符是8bit的,還是16bit的寬度。在早期的版本中,字符的寬度都是8bit的,后來為了支持Unicode字符集,從第5版起,Symbian系統將16bit的字符作為標準。Symbian系統現在支持這兩種字符長度的描述符,除了Copy()和Size()兩個方法以外,這兩種寬度的描述符的行為是完全一致的,這兩個方法的使用,我們后面再介紹。另外,有一套中立的描述符類型,它們既可以被定義為窄的描述符類型,也可以被定義為寬的描述符類型,這要取決于編譯時的寬度。您可以從它的名字上很容易看出這個類型所表示的寬度。假設,它以8結尾(例如:TPtr8,就意味著它表示是的8bit的窄字符,而以16結尾的描述符類(例如:TPtr16)則操作16bit的寬字符。對中立(neutral)的類型來講,沒有數字結尾(例如:TPtr),在Symbian系統第5版以后,默認的情況下,它們表示寬度為16bit的字符串。它們之間的關系比較類似于TInt,TInt16或TInt32之間的關系,這一點應當是比較易于理解的。一般情況下,您沒有必要指明是字符串的寬度,用中立的類型就可以了,這樣使你的代碼易于在寬字符版本和窄字符版本之間轉換(有過編程經驗的朋友應該有這樣的印象,我們平常寫代碼,大多情況下,僅僅使用UINT類型,而較少考慮使用UINT16,UINT32類型)。另外一個問題是:描述符和字面量(literal)的區別。所謂字面量是指在編碼的時候就已經確定的量,例如,標準C中的char*p="Helloworld";其中的"Helloworld"就是字面量。在Symbian系統中,對它們的處理是很不一樣的,這點我們在后面再介紹。有了這樣的一些認識,現在我們可以來看看有哪些描述符類型。在Symbian系統中描述符類型有兩大種類:不可修改(non-modifiable)的描述符和可修改(modifiable)的描述符。3.1不可修改(non-modifiable)的描述符在Symbian系統中,所有的描述符都繼承自TDesC,在前面我們已經討論了類名前綴T所代表的意義,在這里,我們更關心類名的后綴C所代表的意義,這個C是單詞Constant的首字符,表示這個類是不可更改的。這個類提供了一些用來返回字符串的長度和操作數據的方法。Length()方法返回了描述符的長度,因為,每個描述符對象在內存中的布局都是同樣的,用4個字節來表示它所包含的數據的長度(實際上,只用了32個bit中的28個bit,剩余的4bit留作它用,所以描述符能表示的最大的長度為228字節,256MB,不過這已經足夠了)。所以,Length()方法沒有被它的子類重寫,它對所有子類都有效。但是,根據實現子類的方法的不同,子類訪問數據的方式也不一樣,Symbian系統不要求它的子類通過虛函數的方式來實現自己的訪問數據的方法。不用虛函數重寫的原因是因為,虛函數會給每個被派生的描述符對象增加4節字的額外負擔,c++用這4個字節來存放指向虛函數表的指針。我們前面說過,在設計描述符的時候要讓它盡可能高效,額外的字節開銷被認為是不理想的。存放長度的4個字節中,28bit用來表示長度,剩下的4bit用來表示描述符的類型。目前,symbian系統中有5種派生的描述符類型,4bit限制了描述符的種類最多只能有16種,但這已經足夠了。子類可以通過調用基類TDesC的Ptr()方法來訪問描述符的數據,Ptr()方法檢查這4個bit,確定描述符的類型并返回它的數據在內存中的地址。當然,這要求TDesC基類清楚它的子類的內存布局,并在Ptr()方法中使用硬編碼的方法。后面,為了表述上的方便,我們也把這種不可修改的描述符也稱為常量描述符(constantdescriptor)總結:不可修改的描述符類TDesC是所有的非字面量描述符的基類,它提供了確定描述符長度和訪問數據的方法,另外,它實現了所有的您想用來處理常量字符串的操作。3.2可修改(modifiable)的描述符所有的可修改的描述符都從TDes基類派生,而TDes本身又是從TDesC派生的。TDes有一個額外的成員變量用來存放為該描述符分配數據的最大長度。MaxLength()方法返回了這個最大的長度。像TDesC中的Length()方法一樣,MaxLength()方法也不被TDes的子類繼承。 TDes類提供了一系列的方法,用來對可修改字符串數據的操作,包括對字符串的附加、填充和格式化操作。所有的這些方法都被派生類繼承,派生類只實現一些特定的構造方法和復制賦值的方法。這些方法都不負責分配內存,假設它們超過了描述符的數據長度,例如,用Append()方法在某個字符串后面附加另一個字符串時,在調用該方法之前,您必須確保有足夠的內存空間。當然,只要不超過描述符的最大存儲容量,描述符的長度可以自由地伸縮。當描述符長度比最大長度短的時候,描述符的后面局部是多余未用的。這些方法使用了斷言(assertion)來確保描述符的最大長度不會被超出。如果發生內存溢出,將會產生一個panic(關于panic,我們將在后面的章節介紹),這樣可以方便您檢查和修正程序的錯誤。事實上,不可能使描述符溢出,這一點保證了您代碼的強壯性,而且不易產生難以跟蹤的內存陷阱。但需要注意的是,由于基類的構造方法是proteced類型的,所以您無法直接實例化一個TDesC或TDes類的實例。現在我們來看看描述符的派生類,您可以實例化和使用派生類的對象。正如前面所說,這個地方是比較讓人迷惑的,因為描述符存在大量的派生類。前面,我們已經解釋過為什么每個類會有三個不同的版本,例如:TDes8,TDes16和TDes,分別對應窄字符,寬字符和中立的類。現在,讓我們看看有哪些主要的描述符類型,在深入討論每種類型的細節之前,我們先考察一下它們在一般情況下的內存布局。描述符有兩種根本的內存布局:指針描述符和緩存區描述符。不同之處在于,指針描述符持有一個指向字符串的指針,而這個字符串存儲在內存中的基它位置。與指針描述符不同,緩存區描述符本身持有字符數據,也就是說字符數據本身構成了描述符的一局部。總結:TDes是所有的可修改的描述符的基類,并且它自己也是從TDesC派生的。它有一個能返回最大的內存容量的方法和一系列的用來修改字符串數據的方法。3.3指針描述符(pointerdescriptor)指針描述符可分為兩種:TPtrC和TPtr(我們前面說過,每種類型的描述符,按照字符寬度,都可以分為三個版本,例如:窄字符版本TPtrC8,寬字窄版本TPtrC16和中立的版本TPtrC,所以嚴格來講,有六種指針描述符)。指針描述符所持有的字符串是跟描述符本身分開來存放的,它可以被存儲在ROM中,堆中或棧中。由于保存數據的內存既不為描述符所擁有,也不通過它來管理。所以,如果要該描述符是在堆上分配的,那么應通過堆描述符(HBufC,下面將要講解)來操作內存的分配和銷毀;如果指針描述符所指向的字符串是在棧上分配的,那這個內存必須是已經在棧上分配好的。通常情況下,指針描述符是基于棧的,但有時候,它們也可以在堆上使用,例如:作為一個CBase派生類的成員變量的時候。在不可修改的描述符(TPtrC)中,指向數據的指針存放在長度的后面,因此,指針描述符的總長度為2個字(word);在可修改的指針描述符中,它存放在最大長度的后面,因此,總長度為3個字。下列圖比較了TPtr和TPtrC內存布局.TPtrCTPtrC相當于C語言中的constchar*。被它指向的數據可以被訪問但不能被修改:也就是說,描述符中的數據是常量。所有的從基類TDesC中繼承的操作都是可訪問的。TPtrC定義了一系列的構造方法,使得它能從其它的描述符、指向內存的指針或以0結尾的C語言字符串構造。//字面量描述符將在后面介紹_LIT(KLiteralDes,"Sixtyzipperswerequicklypickedfromthewovenjutebag");TPtrCpangramPtr(KLiteralDes);//從字面量描述符構造TPtrCcopyPtr(pangramPtr);//從其它的描述符構造TBufC<100>constBuffer(KLiteralDes);//常量緩存區描述符,后面介紹TPtrCptr(constBuffer);//ConstructedfromaTBufC//TText8isasingle(8-bit)character,equivalenttounsignedcharconstTText8*cString=(TText8*)"Waltz,badnymph,forquickjigsvex"; //從以0結尾的字符串構造TPtrC8anotherPtr(cString);TUint8*memoryLocation;//PointerintomemoryinitializedelsewhereTIntlength;//Lengthofmemorytoberepresented...TPtrC8memPtr(memoryLocation,length);//從一個指針構造。這個指針本身可以改變成指向其他的字符串數據(通過Set()方法)。如果您想指明,不能改變您的TPtrC所指向的數據,那么您可以將TPtrC聲明為const,這樣,當您試圖用Set()方法更改TPtrC所指向的數據時,編譯器會產生警告。//字面量描述符_LIT(KLiteralDes1,"Sixtyzipperswerequicklypickedfromthewovenjutebag");_LIT(KLiteralDes2,"Waltz,badnymph,forquickjigsvex");TPtrCalpha(KLiteralDes1);TPtrCbeta(KLiteralDes2);alpha.Set(KLiteralDes2);//alphapointstothedatainKLiteralDes2beta.Set(KLiteralDes1);//betapointstothedatainKLiteralDes1constTPtrCgamma(beta);//Pointstothedatainbeta,KLiteralDes1gamma.Set(alpha);//Generatesawarning,butpointstoalpha這里應當加一些示范代碼TPtrTPtr是可修改的指針描述符,它可用來訪問和修改字符串或二進制數據。TDesC和TDes所提供的所有的操作都適用于TPtr。這個類定義了一些構造方法,使得它能從指向內存的指針構造,并設置適當的長度值和最大長度值。編譯器也會產生隱式的構造方法和拷貝構造方法,因為它們沒有被聲明為保護的或私有的。一個TPtr對象可以從其它的可修改描述符構造,例如:通過在不可修改的描述符上調用Des()方法,這個方法返回一個如下所示的TPtr對象:_LIT(KLiteralDes1,"Jackdawslovemybigsphinxofquartz");TBufC<60>buf(KLiteralDes1);//TBufCaredescribedlaterTPtrptr(buf.Des());//Copyconstruction;canmodifythedatainbufTIntlength=ptr.Length();//Length=37TIntmaxLength=ptr.MaxLength();//Maximumlength=60,asforbufTUint8*memoryLocation;//Validpointerintomemory...TIntlen=12;//LengthofdatatoberepresentedTIntmaxLen=32;//Maximumlengthtoberepresented//ConstructapointerdescriptorfromapointerintomemoryTPtr8memPtr(memoryLocation,maxLen);//length=0,maxlength=32TPtr8memPtr2(memoryLocation,len,maxLen);//length=12,max=32另外,TPtr提供了賦值運算符=(),用來拷貝數據到指針所指向的內存(數據源可以是可修改、不可修改的指針描述符,或以0結尾的字符串)。如果要拷貝的數據的長度超過了描述符的最大長度,會引發一個系統異常。像TPtrC一樣,TPtr也定義了一個Set()方法,用來改變描述符所指向的數據。_LIT(KLiteralDes1,"Jackdawslovemybigsphinxofquartz");TBufC<60>buf(KLiteralDes1);//TBufCaredescribedlaterTPtrptr(buf.Des());//PointstothecontentsofbufTUint16*memoryLocation;//Validpointerintomemory...TIntmaxLen=40;//MaximumlengthtoberepresentedTPtrmemPtr(memoryLocation,maxLen);//length=12,maxlength=40//CopyandreplacememPtr=ptr;//memPtrdataisKLiteralDes1(37bytes),maxLength=40_LIT(KLiteralDes2,"Thequickbrownfoxjumpsoverthelazydog");TBufC<100>buf2(KLiteralDes2);//TBufCaredescribedlaterTPtrptr2(buf2.Des());//Pointstothedatainbuf//Replacewhatptrpointstoptr.Set(ptr2);//ptrpointstocontentsofbuf2,maxlength=100memPtr=ptr2;//AttempttoupdatememPtrwhichpanicsbecausethe//contentsofptr2(43bytes)exceedsmaxlengthofmemPtr(40bytes)您一定不要混淆了Set()方法和=()賦值操作。前者將描述符的指針重置,使它指向新的數據區域,而后者將數據拷貝到描述符中,一般來說,這會更改描述符的長度,但不會更改它的最大長度值。3.5基于棧(stack-based)的緩沖區描述符基于緩沖區的描述符也可以分為可修改的TBuf和不可修改TBufC的兩種類型。對這種描述符來講,字符串數據本身就是描述符的一局部。下列圖給出了描述符的內存布局:這兩種描述符通常用來存儲定長的或相對較小的字符串,常用來存放長度小于256個字符的文件名。類似于C語言中的char[],但是,它們具有檢查內存溢出的功能。TBufC<n>TBufC<n>是不可修改的緩沖區類型,它主要用來存放字符串常量或是二進制數據。該類從TBufCBase類派生,尖括號<>內的數字表示分配給該描述符的數據區的大小。它定義了一些構造方法,允許從其它的描述符或以0結尾的字符串構造。也允許創立一個空的描述符,然后再填充。由于該描述符的數據是不可修改的,它的整個內容可以被置換(通過該類的所定義的賦值操作),用來置換的數據可以是其它的不可修改的描述符或是0結尾的字符串,但是,無論是何種情況,新數據的長度都不能超過長度n(也就是創立該類的時候指定的模板參數)。_LIT(KPalindrome,"Satan,oscillatemymetallicsonatas");TBufC<50>buf1(KPalindrome);//ConstructedfromliteraldescriptorTBufC<50>buf2(buf1);//Constructedfrombuf1//ConstructedfromaNULL-terminatedCstringTBufC<30>buf3((TText*)"Neveroddoreven");TBufC<50>buf4;//Constructedempty,length=0//Copyandreplacebuf4=buf1;//buf4containsdatacopiedfrombuf1,lengthmodifiedbuf1=buf3;//buf1containsdatacopiedfrombuf3,lengthmodifiedbuf3=buf2;//Panic!Maxlengthofbuf3isinsufficientforbuf2data該描述符中的數據可以被整體置換,但不能被直接修改,但有時候我們確實需要修改緩存區中的數據,該怎么辦呢?系統提供了另一種途徑來修改數據。該類定義了Des()方法,它為緩存區中的數據返回一個可修改的指針描述符(TPtr)。我們可以通過這個指針描述符間接地修改緩沖區中的數據。當數據通過指針描述符被修改以后,指針描述符和緩沖區描述符中的iLength的值會跟著改變,但要記住,緩存區描述符的長度值只可能減小,而是不可能增大的,因為,描述符類是不提供內存管理管理功能的。_LIT8(KPalindrome,"Satan,oscillatemymetallicsonatas");TBufC8<40>buf(KPalindrome);//ConstructedfromliteraldescriptorTPtr8ptr(buf.Des());//dataisthestringinbuf,maxlength=40//Illustratestheuseofptrtocopyandreplacecontentsofbufptr=(TText8*)"DoGeeseseeGod?";ASSERT(ptr.Length()==buf.Length());_LIT8(KPalindrome2,"Arewenotdrawnonward,wefew,drawnonwardtonewera?");ptr=KPalindrome2;//Panic!KPalindrome2exceedsmaxlengthofptr(=40)TBuf<n>這也是一個模板類,它是一個可修改的緩沖區描述符類,后面的<n>表示緩沖區大小。TBuf從TBufBase類派生,而TBufBase是從TDes派生的,因此,它繼承了TDes和TDesC類所有的方法。像TBufC<n>一樣,TBuf<n>也定義了一系列的構造方法和賦值操作。對所有的描述符類型來講,內存管理是您的責任,盡管這個緩沖區中的數據是可修改的,但它的長度不能超過在構造方法中所給定的最大值(n)。假設緩沖區的內容需要擴展,那么您必須決定是在編譯的時候就給定一個足夠大的值,或是在運行的時候動態分配內存。但無論哪種情況,都要確保數據長度不要超過緩存區的最大長度。如果需要使用動態分配的內存,您可以使用基于堆的描述符,這個我們在后面要講到。要是您覺得管理內存分配的任務太過繁重,您也可以選擇使用動態數組。不過,您應當記住,使用動態數組的額外開銷是很高的。_LIT(KPalindrome,"Satan,oscillatemymetallicsonatas");TBuf<40>buf1(KPalindrome);//ConstructedfromliteraldescriptorTBuf<40>buf2(buf1);//ConstructedfromconstantbufferdescriptorTBuf8<40>buf3((TText8*)"DoGeeseseeGod?");//fromCstringTBuf<40>buf4;//Constructedempty,length=0,maximumlength=40//Illustratecopyandreplacebuf4=buf2;//buf2copiedintobuf4,updatinglengthandmaxlengthbuf3=(TText8*)"Murderforajarofredrum";//updatedfromCstring3.6基于堆的(Heap-Based)緩沖區描述符當您要使用非常長的字符串時,有另外一種選擇:基于堆的描述符。它能擁有比它的創立者更長的生存期。當您在編譯的時候還不能確定緩沖區長度的時候,堆描述符也是很有用的,這時,它的作用相當于C語言中的malloc。HBufC也許您已經發現,HBufC的類名以“H〞開頭,這不符合Symbian系統中慣用的命名習慣。這確實是一個特例,“H〞表示這個類一般是在堆(Heap)上分配的。HBufC定義了靜態的NewL()方法,用來在堆上創立一個緩存區。正如您所見到,HBufC中的字母“C〞表示這個表述符是不可修改的。對該類的操作幾乎和TBufC<n>一樣:該類提供了一套賦值操作,允許整個緩沖區中的內容被替換掉;同樣,新內容的長度不能超過緩存區的大小,否則會引起系統異常;通過調用Des()方法,可以返回一個可修改的指針描述符(TPtr),可以通過這個指針描述符來更改緩沖區中的內容。_LIT(KPalindrome,"DoGeeseseeGod?");TBufC<20>stackBuf(KPalindrome);//Allocatesanemptyheapdescriptorofmaxlength20HBufC*heapBuf=HBufC::NewLC(20);TIntlength=heapBuf->Length();//Currentlength=0TPtrptr(heapBuf->Des());//Modificationoftheheapdescriptorptr=stackBuf;//CopiesstackBufcontentsintoheapBuflength=heapBuf->Length();//length=17HBufC*heapBuf2=stackBuf.AllocLC();//Fromstackbufferlength=heapBuf2->Length();//length=17_LIT(KPalindrome2,"Palindrome");*heapBuf2=KPalindrome2;//CopyandreplacedatainheapBuf2length=heapBuf2->Length();//length=10CleanupStack::PopAndDestroy(2,heapBuf);記住,堆描述符可以按您的要求的尺寸動態分配內存,但它不會自動按您的期望更改緩沖區的大小。在修改緩存區的內容之前,您要確保緩存區的內存是足夠的。為了幫您簡化這些操作,HBufC提供的一套ReAllocL()方法,它可以用來擴展堆的緩存區(這個操作有可能會使緩沖區從一個內存區域搬到另一個區域)。IftheHBufC*isstoredonthecleanupstack,movingthepointerasaresultofmemoryreallocationcancausesigni.cantproblemseitherintheeventofaleaveorifthecleanupstack’sPopAndDestroy()functionisusedtodestroythememory.如果您在HBufC上調用Des()方法來獲取了TPtr,在經過重新分配內存后,TPtr中的成員變量iPtr有可能變成無效的。因此,為了確保平安,在重新分配內存后,應該再次調用Des()來創立一個新的TPtr對象。注:出于性能上的考慮,Symbian系統并沒有提供可修改的堆描述符HBuf。總結:Symbian系統中總共有5種類型的描述符,TPtrC,PTtr,TBufC<n>,TBuf<n>和HBufC。下面的圖示說明了它們的繼承關系。3.7字面量描述符(LiteralDescriptors)下面我們來看看字面量描述符,它相當于C語言中的staticchar[]。字面量描述符是通過一系列的宏來創立的,這些宏可在頭文件e32def.H中找到#define_L8(a)(TPtrC8((constTText8*)(a)))#define_S8(a)((constTText8*)a)#define_LIT8(name,s)conststaticTLitC8<sizeof(s)>name={sizeof(s)-1,s}#define_L16(a)(TPtrC16((constTText16*)L##a))#define_S16(a)((constTText16*)L##a)#define_LIT16(name,s)conststaticTLitC16<sizeof(L##s)/2>name={sizeof(L##s)/2-1,L##s}首先,我們來看_LIT,這是最有效率也是被使用得最多的一個。這個宏的用法如下:_LIT(KMyLiteralDescriptor,"Thequickbrownfoxjumpsoverthelazydog");后面KMyLiteralDescriptor就可以作為一個常量來使用,例如可以將它寫到文件或顯示給用戶。_LIT宏構建了一個名為KMyLiteralDescriptor的TLitC16對象,其中保存了字符串的值(在這個例子中是Thequickbrownfoxjumpsoverthelazydog),在二進制程序中可以找到這個值,因為它是被寫到文件中的。如您所料,_LIT8和_LIT16的用法相似。因為描述符的寬度為16bit,所以,在將C字節類型的字符串轉換為描述符能用的數據時,宏將字符串的長度除以2。作為參考,下面給出類TLitC16的定義,其中__TText被定義為寬的,16bit的字符。TLitC8也有類似的定義。template<TIntS>classTLitC16{public:inlineconstTDesC16*operator&()const;inlineoperatorconstTDesC16&()const;inlineconstTDesC16&operator()()const;...//Omittedforclaritypublic:TUintiTypeLength;__TTextiBuf[__Align16(S)];};template<TIntS>inlineconstTDesC16*TLitC16<S>::operator&()const{returnREINTERPRET_CAST(constTDesC16*,this);}template<TIntS>inlineconstTDesC16&TLitC16<S>::operator()()const{return*operator&();}template<TIntS>inlineTLitC16<S>::operatorconstTDesC16&()const{return*operator&();}從上面的定義中可以看到,TLitC16(和TLitC8)并不從TDesC8或TDesC16派生,但是它們與TBufC8或TBufC16具有相同的內存布局。這就使得TLitC16(和TLitC8)可以用在任何可以使用TDesC的地方。您也可以用如下的方法從一個字面量構造一個指針描述符:TPtrC8thePtr(KMyLiteralDescriptor);從字面量構造緩沖區描述符需要一點小技巧。如果您用size()去獲得_LIT常量,它會返回相應的TLitC對象的尺寸大小,這個尺寸相當于描述符內容的尺寸加上額外的8個byte(用來存放長度值的4字節和表示結束符的NULL)。如果您想用它來構造基于堆的描述符,必須要將這額外的8個字節考慮進去。//定義一個包含44字符的字面量_LIT8(KExampleLit8,"Thequickbrownfoxjumpedoverthelazydog");TIntsize=sizeof(KExampleLit8);//52bytes(contents+8bytes)TBufC8<(sizeof(KExampleLit8)-8)>theStackBuffer(KExampleLit8);對基于堆的描述符,您可以用描述符實際內容的長度來分配緩沖區,然后將內容拷貝到描述符中。為了得到正確的長度,您可以用公共(public)的成員變量iTypeLength,或者,也可以用更簡單的方法,使用()操作符來將字面量轉換成一個描述符,然后用這個得到的描述符來得到內容的長度。但最簡單的方法是,使用()操作符將對象轉換成描述符后,直接調用TDes::AllocL()方法,返回一個HBufC*,代碼如下:TIntdescriptorLength=KExampleLit8.iTypeLength;//44bytes//Formastackbufferdescriptoraroundtheliteral//CreateaheapbuffercopyingthecontentsoftheliteralHBufC8*theHeapBuffer=KExampleLit8().AllocL();//對寬字符字面量的操作類似_LIT16(KExampleLit16,"Thequickbrownfoxjumpedoverthelazydog");size=sizeof(KExampleLit16);//96bytes(contentsinbytes+8bytes)descriptorLength=KExampleLit16.iTypeLength;//44bytes(contents)用_L和_LIT生成的字面量,它們的內存布局是有差異的,如下列圖所示:現在我們簡單地看看_L和_S宏,這兩個宏已經過時,但在測試代碼中還經常用到。RDebug::Print(_L("Helloworld!"));這個代碼的作用相當于:_LIT(KLit,"Helloworld!");RDebug::Print(KLit);從上面的代碼可以看到,使用_L的好處在于,您可以直接使用它,而無需在使用之前,在別的地方聲明。字符串(〞Helloworld!〞)被作為一個根本的以0結尾的字符串寫到二進制文件中,它前面沒有長度值(這不同于_LIT產生的字符串)。由于沒有長度值,字面量的內存布局不同于描述符,并且當代碼運行的時候,_L的第個實例都會產生一個臨時的TPtrC,這個TPtrC的指針指向字面量的第一個字節在ROM中的存儲位置。只要是在創立該字面量的生存期中使用這個臨時的描述符,這都是平安的。然而,創立臨時變量要求設置指針、長度和描述符的類型,這對內聯的構造方法來說是一個負擔,如果代碼中有很多這樣的字面量,也會使得二進制程序的體積增大。如果僅從存儲方式上看,_S宏和_L是相同的,但有一點不同它不產生臨時的TPtrC描述符。如果您僅將它作為以0結尾的描述符使用,那么就使用_S宏。到目前為止,我們已經討論了關于描述符的根本知識,包括如何實例化每一種具體的描述符,如何訪問和修改描述符的數據,以及如何置換描述符的內容。現在我們來關注一下操作數據的方法和在使用描述符時一些常見的問題。3.8描述符作參數和返回類型在編寫代碼的時候,您可能不想被限制于只能使用TBuf,原因是僅僅因為某個特定的庫函數要求使用它。同樣的道理,作為函數的提供者,您可能對調用者傳遞進來的參數類型不感興趣。事實上,您不應該要求調用者傳遞特定類型的參數,因為您可能在后面要修改函數的實現,您可能要改變描述符的類型,如果您將這樣的函數作為編程接口,最后您不得不讓您的客戶也改變他們的代碼。這樣的改動是非常不理想的,因為它破壞了代碼的兼容性。除非您來掌管描述符(負責描述符的創立和銷毀工作),您甚至可以不用知道描述符是基于堆的還是基于棧的。事實上,只要標準類型的描述符(我們前面提到的5種描述符類型之一),就可以在它上面調用適當的方法,客戶代碼完全可以忽略描述符的內存布局和它在內存中的位置。基于以上的原因,當您定義函數的時候,應當盡量使用抽象的基類作為函數的參數和返回值。為了有效率,描述符參數應當使用引用傳遞的方式,要么是constTDesC&或者是TDes&。例如,類RFile定義了read()和write()方法IMPORT_CTIntWrite(constTDesC8&aDes);IMPORT_CTIntRead(TDes8&aDes)const;在這兩個方法中,輸入的描述符被顯式地聲明為8bit的寬度,這樣可以既寫入字符串,也可以寫入二進制數據。被用來寫入到文件中的參數是對一個不可修改的描述符的引用,而在讀文件的時候,使用了可修改的描述符的引用。可修改描述符的最大長度決定了可以從文件中讀入多少數據,所以不需要再給文件效勞器傳遞一個表示長度的參數。文件效勞器將會填充滿描述符。當文件中的數據不夠描述符的最大長度時,文件效勞器會把所有可得的數據寫入描述符。調用函數后,描述符的長度反映了寫入數據的長度。這樣,調用者也無需再另外傳遞一個參數用來表示返回的數據長度。當寫一個函數的時候,如果參數是可修改的描述符,實際上您不必考慮它是否有足夠的空間用來存放數據,因為描述符本身有邊界檢查的機制,如果出現了內存溢出現象,會產生系統異常。當然,您也可能不希望在描述符數據區過短的情況下,描述符的方法會發生系統異常。這時,您應當在文檔中說明,如果描述符的長度不夠將會如何處理。有時候,一個比較好的方法是,給調用者返回一個長度值,這樣,調用者可以采用適當的步驟來分配一個正確長度的描述符。HBufC*CPoem::DoGetLineL(TIntaLineNumber){//Codeomittedforclarity.Allocatesandreturnsaheapbuffer//containingthetextofaLineNumber(leavesifaLineNumberis//outofrange)}voidCPoem::GetLineL(TIntaLineNumber,TDes&aDes){HBufC*line=DoGetLineL(aLineNumber);CleanupStack::PushL(line);//Isthedescriptorlargeenough(4bytesormore)toreturnan//integerrepresentingthelengthofdatarequired?if(aDes.MaxLength()<line->Length()){if(aDes.MaxLength()>=sizeof(TInt)){//Writesthelengthrequired(TPckgisdescribedlater)TPckg<TInt>length(line->Length());aDes.Copy(length);}//Leave&indicatethatthecurrentlengthistooshortUser::Leave(KErrOverflow);//LeavesaredescribedinChapter2}else{aDes.Copy(*line);CleanupStack::PopAndDestroy(line);}}另一個方案是,在函數中分配堆緩沖區,把它返還給調用者,由調用者負責銷毀它。3.9常用的方法Ptr()基類TDesC實現了Ptr()方法,用來訪問描述符的數據,該方法返回一個指向字符數組首地址的指針。您可以通過這個指針來直接操作字符串數據。代碼如下所示:Size()和Length()TDesC實現了Size()andLength()方法,前者返回描述符所占有的字節數,而后者返回的是描述符的字符長度。對8bit的描述符來講,它們是相等的,而對16bit的描述來說,Size()返回的數值是Length()的兩倍。MaxLength()可修改的描述符TDes實現的這個方法返回描述符的最大長度。SetLength()和SetMax()前者用來設置描述符的長度,這個長度值必須是小于描述符的最大長度的,否則會引起系統異常。后者將描述符的當前長度設置成最大值,注意,它不并不能擴展描述符數據區的長度。Zero()和FillZ()前者將描述符的長度設置為0,而后者是用0來來填充描述符的內容置。如果您要用其它字符填充描述符的內容,可用Fill()方法。這個方案類似于C語言中的memset()函數。Copy()TDes實現了一系列的重的Copy()方法,下面是其中的兩個:IMPORT_CvoidCopy(constTDesC8&aDes);IMPORT_CvoidCopy(constTDesC16&aDes);這些方法將參數描述符中的數據拷貝到目標描述符中,同時為目標描述符設置新的長度。如可源描述符的長度超過目標描述符的最大長度,將會引發一個系統異常。3.10使用HBufC堆描述符我們已經討論過描述符的一些特性,現在來關注一下使用描述符時經常容易范的錯誤。首先,我們將創立和使用堆描述符HBufC。前面提到過,在已有的描述符上調用Alloc()或AllocL()方法,可以產生一個新的HBufC。這里是一個例子:voidCSampleClass::UnnecessaryCodeL(constTDesC&aDes){iHeapBuffer=HBufC::NewL(aDes.Length());TPtrptr(iHeapBuffer->Des());ptr.Copy(aDes);...//以上代碼完全可以被下面的代替,下面代碼更有效率。iHeapBuffer=aDes.AllocL();}Anothercommonwaytointroducecomplexityoccursintheoppositedirection,thatis,thegenerationofTDesC&fromaheapdescriptor.當從一個堆描述符產生一個TDesC&的時候,也容易范一個錯誤,這個錯誤同樣為代碼增加了復雜性。代碼如下所示:constTDesC&CSampleClass::MoreAccidentalComplexity(){return(iHeapBuffer->Des());//以上代碼完全可以寫成return(*iHeapBuffer);//這樣更簡潔高效}另外一個比較微妙問題是,當您分配一個HBufC以后,然后在它上面調用Des(),可以返回一個TPtr對象。HBufC*buf=HBufC::NewL(9);TPtrp=buf->Des();可是,假設您回憶一下,可以知道在HBufC中,并沒有一個字(word)用來保存最大長度的信息因為HBufC是不可修改的(non-modifiable),它不需要最大長度的信息。然而,,TPtr需要這個最大長度的信息,這時問題來了,您從哪里得到這個最大長度呢?答案在于:當您調用Des()的時候,系統用HBufC的最大
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯系上傳者。文件的所有權益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網頁內容里面會有圖紙預覽,若沒有圖紙預覽就沒有圖紙。
- 4. 未經權益所有人同意不得將文件中的內容挪作商業或盈利用途。
- 5. 人人文庫網僅提供信息存儲空間,僅對用戶上傳內容的表現方式做保護處理,對用戶上傳分享的文檔內容本身不做任何修改或編輯,并不能對任何下載內容負責。
- 6. 下載文件中如有侵權或不適當內容,請與我們聯系,我們立即糾正。
- 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 格林童話精讀課件
- 冷鏈物流設施租賃合同
- 陽光小區幼兒園戶外游樂設施改造施工合同
- 社會責任教育
- 緩解壓力和情緒管理
- 金屬熱處理模擬考試題+答案
- 管理信息系統教案
- 某水利工程混凝土澆筑勞務分包合同
- 工程承包雙方合同管理與執行指南
- 市政道路照明工程勞務合同
- 檢驗科標本運送培訓
- 初中作文指導-景物描寫(課件)
- 秋 輕合金 鋁合金相圖及合金相課件
- 6.3.1 平面向量基本定理 課件(共15張PPT)
- 安全安全檢查表分析(SCL)記錄表(設備、設施)
- 城市濕地公園設計導則2017
- 小學巡課記錄表
- 消防管道隱蔽工程驗收報審表(表格記錄)
- 地質災害群測群防講義
- 高頻變壓器標準工時對照表
- 232425黃昆固體物理教案
評論
0/150
提交評論