Wiley.Symbian.OS.C.plus.plus.for.Mobile.Phones.Aug.2007 (779890), страница 21
Текст из файла (страница 21)
Trust CBase to zero-initialize all data, includingall pointers. Utilize CBase’s virtual destructor for cleanup purposes.• Never leave from a C++ constructor.• Never allocate memory from within a C++ constructor.• Put construction functions that might leave into a second-phase constructor such as ConstructL().86OBJECTS – MEMORY MANAGEMENT, CLEANUP AND ERROR HANDLING• Instead of expecting your user to call ConstructL() and/or aC++ constructor explicitly, use NewL() and NewLC() to wrap upallocation and construction.
You can enforce this by making the C++constructors private.It doesn’t take long to become familiar with these rules and to workwith them effectively. In addition, it is easy to trap many forms ofmisbehavior by using the emulator debug keys provided by the UI andthe heap checking provided by CONE.5DescriptorsOne of the most common and recurring programming requirements incomputing is the handling of strings. In Symbian OS, strings are handledusing a set of classes known as descriptors. They offer a safe and consistentmechanism for dealing not only with strings but also with binary data.When you encounter descriptors for the first time you may sufferculture shock and confusion at the bewildering number of descriptorclasses to choose from.
When you first start to experiment with themyour code may not compile when you think it should; or it may panicwhen executed; or you may not get the behavior you were expecting.Frustration can easily set in and it may be tempting to use standard Cstring-handling functionality instead. Don’t. Descriptors are one of themost fundamental and heavily used entities in Symbian OS and it isessential to become familiar with them as their use is unavoidable, evenif developing the simplest of native Symbian OS applications.This chapter introduces you to the various types of descriptors andprovides guidance on their usage.5.1 OverviewSymbian OS was based upon an earlier operating system known asEPOC16 (also known as SIBO) which was written in C and used standardC-like string-handling mechanisms.
The development of EPOC16 wasoften held up for days as obscure defects and system lockups had tobe debugged. In many cases, the root cause of these defects was asa consequence of memory overruns. When Symbian OS was beingdesigned, it was deemed vitally important that it would not suffer fromthe same problems and a decision was made to impose a systematic lowlevel solution – the outcome being the creation of the descriptor classeswhich were designed to offer protection against memory overruns.88DESCRIPTORSTDesCTDesTBufBaseTBufTBufCBaseTPtrRBufTPtrCTBufCHBufCFigure 5.1 Descriptor hierarchyThe descriptor classes inherit from the base class TDesC, as shownin Figure 5.1, and they can be classified according to how their data isstored and accessed:• stack descriptors store their data as part of themselves• pointer descriptors point to data stored elsewhere• heap descriptors store their data in the heap.In addition there is a related type known as a literal.
You frequentlysee literals referred to as literal descriptors; because they are stored in theprogram-binary code for an executable, they are also sometimes referredto as program-binary descriptors.Literals aren’t actually descriptors as they do not inherit from the basedescriptor class TDesC; however they are closely related and have anoperator()() method for converting them to a descriptor.There are four ‘abstract’ classes: TDesC, TDes, TBufBase andTBufCBase. These are not abstract in the typical C++ sense, as descriptors do not have pure virtual methods; however, they are abstract in thesense that they are not intended to be instantiated.TBufCBase and TBufBase are an implementation convenience andare not discussed further here.
There are six concrete descriptor types:TBuf, TPtr, RBuf, TPtrC, TBufC and HBufC, which, along with theliteral type, can be instantiated and used directly in your programs tostore or point to your string or binary data.Table 5.1 shows these types along with the approximate standard Cequivalent for the benefit of readers familiar with string handling in thatlanguage.OVERVIEW89Table 5.1 Descriptor typesTypeLiteralStackConstnessNameApproximate C equivalentNotmodifiableTLitCstatic const char []ModifiableTBufNot directlymodifiableTBufCModifiableTPtrNotmodifiableTPtrCModifiableRBufchar* (pointing to owned data)Not directlymodifiableHBufCconst char* (pointingto owned data)PointerHeapchar []const char []char* (pointing tonon-owned data)const char* (pointingto non-owned data)The C in the type name stands for ‘constant’, however the term doesnot mean ‘constant’ in the usual C++ meaning of the term.
TBuf, TPtrand RBuf are all directly modifiable, while TLitC and TPtrC are notmodifiable at all, however TBufC and HBufC are not directly modifiablebut they are indirectly modifiable.Descriptors were designed to be as efficient as possible and a manifestation of this is in TBufC and HBufC’s apparently contradictory ‘constantyet modifiable’ nature.If you need a truly constant descriptor then use TLitC, TPtrC orTBufC as appropriate according to your intended usage.
If you needa modifiable descriptor then choose between a TPtr, TBuf, RBuf orHBufC.From an efficiency point of view, there are basically three points atwhich a decision can be made:• deciding whether to use a stack or heap descriptor to store the data(pointer descriptors do not store any data)• if using a heap descriptor, deciding whether to use an HBufC or RBuf• if using a writable stack descriptor, deciding whether to use thedirectly-writable TBuf or the indirectly-writable TBufC.It is regarding this last choice that the constness or efficiency of thedescriptor classes come into play.
Perhaps the simplest and best advice90DESCRIPTORSregarding these aspects is: unless you are using large numbers of stackdescriptors, do not worry about whether a TBufC or TBuf is moreefficient.Here’s a preview of using descriptors, with a variation on a traditionalexample: the string ‘Hello World!’ is displayed seven times to the textconsole:#include <e32base.h>#include <e32cons.h>void HelloWorldL(void){// Create a literal descriptor_LIT(KTxtHelloWorld, "Hello World!");const TInt KHelloWorldLength = 12;// create aTBufTBuf<KHelloWorldLength> tbuf(KTxtHelloWorld);// create a TBufCTBufC<KHelloWorldLength> tbufc(KTxtHelloWorld);// create an HBufCHBufC* hbufc = KTxtHelloWorld().AllocLC();// Create an RBufRBuf rbuf;rbuf.CreateL(KHelloWorldLength, KTxtHelloWorld);rbuf.CleanupClosePushL();// create a TPtrCTPtrC tptrc(tbufc);// create a TPtrTPtr tptr = hbufc->Des();// display "Hello World!" 7 times to the text consoleCConsoleBase* console = Console::NewL(KTxtHelloWorld,TSize(KConsFullScreen,KConsFullScreen));console->Printf(KTxtHelloWorld);console->Printf(tbuf);console->Printf(tbufc);console->Printf(tptr);console->Printf(tptrc);console->Printf(*hbufc);console->Printf(rbuf);console->Getch(); // pause until user enters inputCleanupStack::PopAndDestroy(2);delete console;}GLDEF_C TInt E32Main(){static CTrapCleanup* cleanup = CTrapCleanup::New();ANATOMY OF DESCRIPTORS91TRAPD(ret,HelloWorldL());delete cleanup;return (0);}5.2 Anatomy of DescriptorsBefore describing how to use the descriptors, we take a look at theirinternal structure as this helps in understanding their differences.As we have already seen, all the descriptors derive from the abstractbase class TDesC.
This class defines two property values: one for storingthe type of the descriptor-memory layout (stored in half a byte) and onefor storing the length of the descriptor (stored in three and a half bytes1 ).TDes inherits from TDesC and additionally stores the maximum lengthof the data that is stored or pointed to by the descriptor.As indicated in Table 5.2, the type field is not used to store the actualtype of the descriptor, instead it is used to store the type of memoryTable 5.2 Types of descriptor layoutTypeDescriptors0HBufC, TBufC, TLitC1TPtrC2TPtr, RBuf3TBuf4TPtr, RBufMeaningArray-type storage withoutmaximum lengthPointer-type storagewithout maximum lengthPointer-type storage withmaximum length, pointingto dataArray-type storage withmaximum lengthPointer-type storage withmaximum length, pointingto a descriptor.1As the type and the length are stored as bit-fields, if you are visually inspecting adescriptor object’s length in an IDE, you must take this into consideration.
For example,with the following code:_LIT(KTxtHello, "Hello");TBuf<5> buf(KTxtHello);The value of the buf object’s iLength data member (where the type and length information is held) is 805306373. In binary this is 00110000000000000000000000000101 – 0011represents the type and 101 the length.92DESCRIPTORSlayout of the descriptor, determining how and where the descriptor datais stored and whether the maximum length is stored.
This allows concretedescriptors to be passed as TDesC or TDes parameters without the overhead of using virtual methods because the TDesC::Ptr() method uses aswitch statement to identify the type of descriptor and thus knows wherethe data is located. This requires that TDesC::Ptr() has hard-codedknowledge of the memory layout of all its subclasses which consequentlymeans that you can’t create your own descriptor class deriving fromTDesC. The actual descriptor memory layouts are shown in Figure 5.2.TDesCTypeLengthTDesTypeLengthMax lengthTLitCType (0)LengthDataTBufCType (0)LengthDataHBufCType (0)LengthDataTPtrCType (1)LengthPointer todataTBufType (3)LengthMax lengthDataType (2 or 4)LengthMax lengthPointer to dataor pointer todescriptorRBuf Type (2 or 4)LengthMax lengthPointer to data orpointer to HBufCTPtrFigure 5.2 Descriptor memory layoutsThe length field is used to store the length of the data contained orpointed to by the descriptor.