Symbian OS Explained - Effective C++ Programming For Smartphones (2005) (779885), страница 16
Текст из файла (страница 16)
TDes has anadditional member variable to store the maximum length of data allowedfor the current memory allocated to the descriptor. The MaxLength()method of TDes returns this value. Like the Length() method of TDesC,it is not overridden by the derived classes.TDes defines the range of methods you’d expect for modifiable stringdata, including those to append, fill and format the descriptor data. Again,all the manipulation code is inherited by the derived classes, and actson them regardless of their type. Typically, the derived descriptors onlyimplement specific methods for construction and copy assignment.None of the methods allocates memory, so if they extend the lengthof the data in the descriptor, as Append() does, for example, you mustensure that there is sufficient memory available for them to succeedbefore calling them.
Of course, the length of the descriptor can be lessthan the maximum length allowed and the contents of the descriptor canshrink and expand, as long as the length does not exceed the maximumlength. When the length of the descriptor contents is shorter than themaximum length, the final portion of the descriptor is simply unused.The modification methods use assertion statements to check that themaximum length of the descriptor is sufficient for the operation to succeed.These will panic if an overflow would occur if they proceeded, allowingyou to detect and fix the programming error (panics are described indetail in Chapter 15 and assertions in Chapter 16).The very fact that you can’t overflow the descriptor makes the coderobust and less prone to hard-to-trace memory scribbles.
In general,descriptor classes use __ASSERT_ALWAYS to check that there is sufficientmemory allocated for an operation, raising a USER category panic if theassertion fails. In the event of such a panic, it can be assumed that noillegal access of memory has taken place and that no data was movedor corrupted.The base classes provide and implement the APIs for constant andmodifiable descriptor operations for consistency, regardless of the actualtype of the derived descriptor. For this reason, the base classes should beMODIFIABLE DESCRIPTORS59used as arguments to functions and return types, allowing descriptors tobe passed around in code without forcing a dependency on a particulartype.
However, if you attempt to create objects of type TDesC and TDesyou’ll find that they cannot be instantiated directly because their defaultconstructors are protected.3So it’s to the derived descriptor types that we now turn, since theseare the descriptor classes that you’ll actually instantiate and use. As Imentioned earlier, it can at first sight appear quite confusing becausethere is a proliferation of descriptor classes. I’ve already explained whythere are three versions of each class, e.g.
TDes8, TDes16 and TDes,for narrow, wide and neutral (implicitly wide) classes respectively. Let’snow look at the main descriptor types, initially considering their generallayout in memory before moving on to look at each class in more detail.I’ll describe the differences between the classes and the methods eachdefines over and above those provided by the TDesC and TDes baseclasses. The following chapter will go further into how to use the baseclass APIs, as well as noting any useful tips or mistakes commonly madewhen doing so.
For comprehensive information about the descriptor APIs,you should refer to the SDK documentation.As I’ll describe, descriptors come in two basic layouts: pointer descriptors, in which the descriptor holds a pointer to the location of a characterstring stored elsewhere, and buffer descriptors, where the string of characters forms part of the descriptor.TDes is the base class for all modifiable descriptors, and itselfderives from TDesC. It has a method to return the maximumamount of memory currently allocated to hold data, and a range ofmethods for modifying string data.When using descriptors, memory management is your responsibility. Descriptors do not perform allocation, re-allocation or garbagecollection, because of the extra overhead that would carry. However, descriptor functions do check against access beyond the endof the data, and will panic if passed out-of-bounds parameters.3There is no copy constructor declared, so the compiler generates a default publicversion which can be used to instantiate a TDes or TDesC by copy, although you areunlikely to have a valid reason for doing this:_LIT(KExampleLiteral, "The quick brown fox jumps over the lazy dog");TPtrC original(KExampleLiteral);TDesC copy(original); // Shallow copy the type, length & data60DESCRIPTORS: SYMBIAN OS STRINGS5.3 Pointer DescriptorsThe string data of a pointer descriptor is separate from the descriptorobject itself and can be stored in ROM, on the heap or on the stack.
Thememory that holds the data is not ”owned” by the descriptor and is notmanaged through it. Thus, if it is on the heap, the memory is created,reallocated if necessary, and destroyed using a heap descriptor pointer(HBufC, described below). If a pointer descriptor is referencing a stackbased string, the memory in question will already have been allocatedon the stack. The pointer descriptors themselves are usually stack-based,but they can be used on the heap, for example as a member variable ofa CBase-derived class. Pointer descriptors are agnostic about where thememory they point to is actually stored.In a non-modifiable pointer descriptor (TPtrC), the pointer to the datafollows the length word, thus the total size of the descriptor object is twowords.
In a modifiable pointer descriptor (TPtr), it follows the maximumlength word and the descriptor object is three words in length. Figure 5.1compares the memory layouts of TPtr and TPtrC.TPtrCiLength12iPtrTDesCTPtrCHello World!ROM, heap or stackTPtriLength12iMaxLength12iPtrTDesCTDesTPtrFigure 5.1 Memory layouts of pointer descriptorsTPtrCTPtrC is the equivalent of using const char* when handling stringsin C.
The data can be accessed but not modified: that is, the data in thedescriptor is constant. All the non-modifiable operations defined in theTDesC base class are accessible to objects of type TPtrC. The class alsodefines a range of constructors to allow TPtrC to be constructed fromanother descriptor, a pointer into memory or a zero-terminated C string.POINTER DESCRIPTORS61// Literal descriptors are described later in this chapter_LIT(KLiteralDes, "Sixty zippers were quickly picked from the wovenjute bag");TPtrC pangramPtr(KLiteralDes); // Constructed from a literal descriptorTPtrC copyPtr(pangramPtr);// Copy constructed from another TPtrCTBufC<100> constBuffer(KLiteralDes); // Constant buffer descriptorTPtrC ptr(constBuffer);// Constructed from a TBufC// TText8 is a single (8-bit) character, equivalent to unsigned charconst TText8* cString = (TText8*)"Waltz, bad nymph, for quick jigsvex";// Constructed from a zero-terminated C stringTPtrC8 anotherPtr(cString);TUint8* memoryLocation; // Pointer into memory initialized elsewhereTInt length;// Length of memory to be represented...TPtrC8 memPtr(memoryLocation,length); // Constructed from a pointerThe pointer itself may be changed to point at different string data – theSet() methods in TPtrC are defined for that purpose.
If you want toindicate that the data your TPtrC points at should not be changed,you can declare the TPtrC to be const, which typically generates acompiler warning if an attempt is made to call Set() upon it. It will notfail, however, since the rules of const-ness in C++ are such that bothconst and non-const functions may be called on a const object.// Literal descriptors are described later in this chapter_LIT(KLiteralDes1, "Sixty zippers were quickly picked from the woven jutebag");_LIT(KLiteralDes2, "Waltz, bad nymph, for quick jigs vex");TPtrC alpha(KLiteralDes1);TPtrC beta(KLiteralDes2);alpha.Set(KLiteralDes2); // alpha points to the data in KLiteralDes2beta.Set(KLiteralDes1); // beta points to the data in KLiteralDes1const TPtrC gamma(beta); // Points to the data in beta, KLiteralDes1gamma.Set(alpha);// Generates a warning, but points to alphaTPtrTPtr is the modifiable pointer descriptor class for access to and modification of a character string or binary data.
All the modifiable andnon-modifiable base class operations of TDes and TDesC respectivelymay be performed on a TPtr.62DESCRIPTORS: SYMBIAN OS STRINGSThe class defines constructors to allow objects of type TPtr to beconstructed from a pointer into an address in memory, setting the lengthand maximum length as appropriate.The compiler also generates implicit default and copy constructors, since they are not explicitly declared protected or private in theclass. A TPtr object may be copy constructed from another modifiable pointer descriptor, for example, by calling the Des() method on anon-modifiable buffer, which returns a TPtr as shown below:_LIT(KLiteralDes1, "Jackdaws love my big sphinx of quartz");TBufC<60> buf(KLiteralDes1); // TBufC are described laterTPtr ptr(buf.Des()); // Copy construction; can modify the data in bufTInt length = ptr.Length();// Length = 12TInt maxLength = ptr.MaxLength(); // Maximum length = 60, as for bufTUint8* memoryLocation;...TInt len = 12;TInt maxLen = 32;// Valid pointer into memory// Length of data to be represented// Maximum length to be represented// Construct a pointer descriptor from a pointer into memoryTPtr8 memPtr(memoryLocation, maxLen); // length = 0, max length = 32TPtr8 memPtr2(memoryLocation, len, maxLen); // length = 12, max = 32In addition, the class provides an assignment operator, operator =(),to copy data into the memory referenced by the pointer (from anothermodifiable pointer descriptor, a non-modifiable pointer or a zero-terminated string).
If the length of the data to be copied exceeds the maximumlength of the descriptor, a panic will be raised. Like TPtrC, this class alsodefines a Set() method to change the descriptor to point at differentdata._LIT(KLiteralDes1, "Jackdaws love my big sphinx of quartz");TBufC<60> buf(KLiteralDes1); // TBufC are described laterTPtr ptr(buf.Des());// Points to the contents of bufTUint16* memoryLocation;// Valid pointer into memory...TInt maxLen = 40; // Maximum length to be representedTPtr memPtr(memoryLocation, maxLen); // length = 12, max length = 40// Copy and replacememPtr = ptr; // memPtr data is KLiteralDes1 (37 bytes), maxLength = 40_LIT(KLiteralDes2, "The quick brown fox jumps over the lazy dog");TBufC<100> buf2(KLiteralDes2); // TBufC are described laterTPtr ptr2(buf2.Des());// Points to the data in bufSTACK-BASED BUFFER DESCRIPTORS63// Replace what ptr points toptr.Set(ptr2); // ptr points to contents of buf2, max length = 100memPtr = ptr2; // Attempt to update memPtr which panics because the// contents of ptr2 (43 bytes) exceeds max length of memPtr (40 bytes)You should be careful not to confuse Set(), which resets yourdescriptor to point at a new data area (with corresponding modification to the length and maximum length members) withoperator =() which merely copies data into the existing descriptor (and may modify the descriptor length but not its maximum length).5.4 Stack-Based Buffer DescriptorsThe stack-based buffer descriptors may be modifiable or non-modifiable.The string data forms part of the descriptor object, located after the lengthword in a non-modifiable descriptor and after the maximum length wordin a modifiable buffer descriptor.