Wiley.Symbian.OS.C.plus.plus.for.Mobile.Phones.Aug.2007 (779890), страница 28
Текст из файла (страница 28)
The descriptor must have amaximum length at least twice its current length. Notice the difference inlengths between that of aDes and that of narrowHello after Expand()has been called.If you use this function, you must be aware that if the descriptoris a pointer descriptor then the memory pointed to (i.e., the result ofcalling TDesC::Ptr()) must be at an even-numbered address becausethe ARM processor can only access 16-bit quantities at even-numberedaddresses.5.11Correct Use of DescriptorsDo Not Declare TDesC or TDes VariablesNeither TDesC nor TDes provide any storage space for data or for apointer to data (see Figure 5.2) thus you should never instantiate a TDesCor TDes.
The following code illustrates a common mistake:void Foo(const TDesC& aDes){TBuf<32> buf(aDes); // buf contains random data}_LIT(KTxtHelloWorld, "Hello World!");TDes des(KTxtHelloWorld);// these compile but neither holdTDesC desC(KTxtHelloWorld);// nor reference any actual dataFoo(desC);Always Pass TDes and TDesC Parameters by ReferenceWhen declaring method parameters, you must declare TDesC and TDesas pass-by-reference and not pass-by-value. The reason is that theseclasses do not contain any actual string data (see Figure 5.2) and if youpass them by value, then the rules of C++ mean that static binding isbeing used and the consequence is that polymorphism won’t work.
TheCORRECT USE OF DESCRIPTORS129end result is that your code compiles but you are not actually passing anydata. For example, in the following code the buffer variable containsrandom stack data:void Foo(const TDesC aString) // error, should be const TDesC&{TBufC<10> buffer(aString); // buffer will contain random data...}Do Not Omit const when Declaring TDesC ParametersIt is a mistake to omit the const keyword when passing a TDesC.The following code may generate a compilation error depending uponthe compiler used.
Even if the method declaration does not generate acompilation error, attempting to pass a literal to that method does, soalways use const:void Foo(TDesC& aString);...Foo(KNullDesC);// possible compilation error// definite compilation errorDo Not Set the Size of a Stack Descriptor at Run TimeA frequent mistake when first using stack descriptors is to attempt todefine their size at run time:TInt length = someValue;TBuf<length> myDescriptor;This generates a compilation error because the stack classes aretemplated and thus their size must be determined at compilation timeand not at run time.10Do Not Place Large Stack Descriptors on the StackIf you find yourself in the situation where you want to define the size ofa stack descriptor at run time, you may be tempted to declare a stackdescriptor with a large enough maximum size to account for the totalamount of data your descriptor may hold:10Depending upon the compiler used, the compilation error may be confusing.
Somecompilers may indicate that the error is in the class declaration of TBuf within thee32std.h header file.130DESCRIPTORSTBuf<5000> myDescriptor;This may be wasteful because it would be consuming 10 000 bytes ofdescriptor stack space regardless of how much data the descriptor actuallycontained (see Section 5.7 if you were expecting it to use 5000 bytes ofstack space).Stack descriptors are typically used on the stack, however, as with anytype, large ones should not use the stack due to the limited default stacksize.11 If you find yourself wanting to use a large stack descriptor see thenext section.A Symbian OS rule of thumb is that if something is more than 256 bytesthen it shouldn’t go on the stack (the stack size is pretty limited: the defaultis 8 KB).
So, for example, if you want to use a TBuf<256> as a stackvariable, you should consider turning it into an RBuf or HBufC instead.Symbian OS defines a number of classes and typedef s that aredescriptors or contain descriptors and that could be used inefficiently ifused on the stack.
It is advisable therefore to be aware of the amount ofspace the following types consume:• a TEntry consumes 552 bytes• a TFileName consumes 520 bytes• a TFullName consumes 520 bytes• a TName consumes 264 bytes.Do Not Allocate a Large Stack Descriptor Directly on the HeapIf you find yourself in the situation where you want to place a largedescriptor on the stack, you may be tempted to allocate the descriptoron the heap, either directly as in this code or indirectly by making it amember variable of a CBase -derived class.TBuf<5000>* myDescriptor = new(ELeave) TBuf<5000>;// allocation on the heapWhile this moves the descriptor data-storage area from the stack to themore plentiful heap, it is still wasteful of memory if the actual amount ofdata you store in the descriptor is much less than 5000 characters.If you find yourself in this situation then the solution is to use one ofthe heap descriptors, HBufC or RBuf, instead.11The stack in Symbian OS is a limited resource and should not be used to hold dataobjects as large as this, even if only temporarily.
(If your code executes on the emulator butgenerates a Kern Exec 3 panic on the hardware, a large descriptor on the stack may be thecause.)CORRECT USE OF DESCRIPTORS131Be Wary of TPtr::operator=()If you find yourself using TPtr’s assignment operator be aware thatattempting to assign a type 4 TPtr into a type 2 TPtr results in a panic.Assigning a type 4 TPtr to a type 2 TPtr does not result in the typechanging, which may lead to problems.12TText* helloText = (TText*)L"Hello";TBufC<8> worldBuffer(_L("World\0"));TInt helloTextLength = User::StringLength(helloText); // length is 6TPtr type2Ptr(helloText, helloTextLength, helloTextLength);TPtr type4Ptr = worldBuffer.Des();type2Ptr = type4Ptr;// program crashtype4Ptr = type2Ptr;// type4Ptr remains a type 4 TPtrtype2Ptr.Set(type4Ptr); // type2Ptr is now a type 4 TPtrBe Wary of TPtr’s Non-conformance to C++ ConventionsYou should be aware that there is some unexpected behavior with TPtr sbecause their copy constructors behave differently from their assignmentoperators.
This can be confusing because it is conventional in C++ toensure that these two operations produce the same result, however TPtrbreaks with this convention.Instead of copying the members of a descriptor, which would resultin two TPtrs pointing to the same data, the TPtr type 4 assignmentoperator is defined to copy the contents of one TDesC into where theTPtr points:_LIT(KWorld, "World");TText helloBuffer[5] = {‘h’,‘e’,‘l’,‘l’,‘o’};TPtr memoryPtr(helloBuffer, 5); // points to "hello"TBufC<5> worldBuf(KWorld);TPtr worldPtr(worldBuf.Des());// points to "World"memoryPtr = worldPtr;// "World" is written to helloBufferNote that this behavior doesn’t occur if the assigning TPtr points tomemory as opposed to a descriptor (i.e., a type 2 TPtr):TText helloBuffer[5] = {‘h’,‘e’,‘l’,‘l’,‘o’};TPtr memoryPtr(helloBuffer, 10); // points to "hello"12This example also illustrates the User::StringLength() method, which returnsthe length of a C-style NULL-terminated string.
There are two overloads: User::StringLength(const TUint8 *aString) and User::StringLength(constTUint16 *aString). In the example code, the L macro is used to indicate to thecompiler that "Hello" is a wide string; without it helloTextLength would be 3. Notethat L is not a macro defined in Symbian OS and should not be confused with _L.132DESCRIPTORSTText worldBuffer[5] = {‘W’,‘o’,‘r’,‘l’,‘d’};TPtr worldPtr(worldBuffer,5);// points to "World"memoryPtr = worldPtr;// "World" is not written to helloBufferUse HBufC::Des() CorrectlyThere is a common mistake that many people tend to make once theybecome familiar with the Des() method. When they have an HBufCthat is to be passed to a method that takes a const TDesC& type, theyassume that they first have to create a TPtr using Des() and passthis into the method.
While this works, it is far simpler and much moreefficient to simply dereference the heap descriptor; for example, if youhave a function foo() prototyped as:void foo(const TDesC& aDescriptor);then the following code works fine:HBufC* buffer = HBufC::NewL(256);...foo(*buffer);Similarly, people frequently also call the Des() method unnecessarilywhen returning an HBufC data from a method, for example, in thefollowing, iData->Des() should be simply *iData:const TDesC& CSomeClass::Data() const{if (iData)// iData is an HBufC*return iData->Des();// should use return *iData insteadelsereturn KNullDesC; 13}Use Alloc() When Creating an HBufC from a DescriptorWhen you have a descriptor and you wish to create an HBufC and copythe contents into it, a common mistake is to code it as follows:void myFunctionL(const TDesC& aBuffer){HBufC* myData = HBufC::NewL(aBuffer.Length());13Note that, with some compilers in some circumstances, an error may be generatedwhen using KNullDesC; if this occurs, use KNullDesC() instead.CORRECT USE OF DESCRIPTORS133TPtr ptr = myData->Des();ptr.Copy(aBuffer);...}Instead use the Alloc(), AllocL() or AllocLC() methods:void myFunctionL(const TDesC& aBuffer){HBufC* myData = aBuffer.AllocLC();...}Be Wary of HBufC::ReAllocL() and HBufC::ReAlloc()If you wish to resize an HBufC you should be aware that ReAllocL()may return a different address from the current HBufC pointer:_LIT(KTxtHello, "Hello");_LIT(KTxtGreeting, "How are you?");HBufC* heapBuf = KTxtHello().AllocLC();// place it on the cleanup stackTPtr ptr(heapBuf->Des());// ptr = KTxtGreeting; // would panic because the// maximum length is exceededheapBuf = heapBuf->ReAllocL(KTxtGreeting().Length());// So need to reallocate the heapBuf variableCleanupStack::Pop();// The address of heapBuf may have changed during ReAllocLCleanupStack::PushL(heapBuf);// so need to re-push it to the cleanup stackptr.Set(heapBuf->Des()); // and reset the pointerptr = KTxtGreeting;// Now the assignment can be made safely...CleanupStack::PopAndDestroy();The call to ReAllocL() creates a new heap descriptor and, if successful, copies the original data into it and returns the new address.
Theoriginal copy is deleted. Of course, if there is insufficient memory, thenthe method leaves. After the reallocation has taken place, heapBuf maypoint to a different heap cell. There may be times when it points tothe original heap cell, but it is safer to assume that it doesn’t. This isparticularly important if you use the Des() method, in which case youneed to re-Set() any TPtr returned by Des().This example also shows how you must pay attention if you are pushingHBufC s to the cleanup stack and you reallocate them. If the memoryaddress changes during the reallocation then an out-of-date memory134DESCRIPTORSlocation may now be held on the cleanup stack; this must be addressed bypopping it off the stack and pushing a new address on.
Note that it wouldhave been incorrect to call CleanupStack::Pop(heapBuf) becausethe system uses the parameter to Pop() as a check that the expecteditem is being popped off the stack. However, if heapBuf’s address haschanged, this results in a panic. Similarly calling PopAndDestroy()with the old value of heapBuf on the stack results in a User-44 panicif its address changes.Be careful not to Pop() from the cleanup stack before the reallocation, if the code from above is rearranged as follows and the call toReAllocL() fails, there will be a memory leak:CleanupStack::Pop();heapBuf = heapBuf->ReAllocL(KTxtGreeting().Length());CleanupStack::PushL(heapBuf);Don’t forget that ReAlloc() and ReAllocL() return a HBufC*which must be assigned to a variable.
The following code also causesa User-44 panic if the address of the allocated cell is changed; this isbecause the old address is being pushed onto the cleanup stack:_LIT(KTxtHello, "Hello");HBufC* hbuf = KTxtHello().AllocL();hbuf->ReAllocL(50); // should be hbuf = hbuf->ReAllocL(50);CleanupStack::Pop(hbuf);CleanupStack::PushL(hbuf);Be aware also that if ReAlloc() is being used and the allocationfails, then NULL is returned so this should be checked for:HBufC* heapBuf = KTxtHello().AllocL();heapBuf = heapBuf->ReAlloc(KSomeLength);TPtr ptr = heapBuf->Des();// Kern Exec 3 if ReAlloc() fails// to allocate memoryAdditional care must be taken if re-allocation is occurring within amethod. For example, consider the following code:void copyTextL(HBufC*& aDes){_LIT(KTxtHello, "Hello");if (aDes->Des().MaxLength() < KTxtHello().Length()){aDes = aDes->ReAllocL(KTxtHello().Length());// may change the address of the parameteraDes->Des() = aSource;}}CORRECT USE OF DESCRIPTORS135HBufC* hBuf = HBufC::NewL(3);...copyTextL(hBuf);The call to ReAllocL() may change the address of the parameter,but the copyTextL() function cannot know if the HBufC has beenallocated to the cleanup stack or not.