Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Symbian OS Explained - Effective C++ Programming For Smartphones (2005) [eng].pdf
Скачиваний:
60
Добавлен:
16.08.2013
Размер:
2.62 Mб
Скачать

HEAP-BASED BUFFER DESCRIPTORS

65

ASSERT(ptr.Length()==buf.Length());

_LIT8(KPalindrome2, "Are we not drawn onward, we few, drawn onward to

new era?");

ptr = KPalindrome2; // Panic! KPalindrome2 exceeds max length of ptr(=40)

TBuf<n>

Like the corresponding non-modifiable buffer class, this class for nonconstant buffer data is a thin template class, the integer value determining the maximum allowed length of the buffer. It derives from TBufBase, which itself derives from TDes, thus inheriting the full range of descriptor operations in TDes and TDesC. TBuf<n> defines a number of constructors and assignment operators, similar to those offered by its non-modifiable counterpart.

As with all descriptors, its memory management is your responsibility and, although the buffer is modifiable, it cannot be extended beyond the initial maximum length set on construction. If the contents of the buffer need to expand, it’s up to you to make sure that you either make the original allocation large enough at compile time or dynamically allocate the descriptor at runtime. The only way you can do the latter is to use a heap descriptor, described below. If this responsibility is too onerous and you want the resizing done for you, I suggest you consider using a dynamic buffer, as described in Chapter 7, bearing in mind that the associated overhead will be higher.

_LIT(KPalindrome, "Satan, oscillate my metallic

sonatas");

TBuf<40> buf1(KPalindrome); //

Constructed from

literal descriptor

TBuf<40> buf2(buf1);

//

Constructed from

constant buffer descriptor

TBuf8<40> buf3((TText8*)"Do Geese see God?"); // from C string TBuf<40> buf4; // Constructed empty, length = 0, maximum length = 40

// Illustrate copy and replace

buf4 = buf2; // buf2 copied into buf4, updating length and max length buf3 = (TText8*)"Murder for a jar of red rum"; // updated from C string

The stack-based buffer descriptors, TBuf<n> and TBufC<n>, are useful for fixed-size or relatively small strings, say up to the length of a 256-character filename.

5.5 Heap-Based Buffer Descriptors

Heap-based descriptors can be used for string data that isn’t in ROM and is not placed on the stack because it is too big. Heap-based descriptors

66

DESCRIPTORS: SYMBIAN OS STRINGS

can be used where they may have a longer lifetime than their creator, for example, passed to an asynchronous function. They are also useful where the length of a buffer to be created is not known at compile time and are used where malloc’d data would be used in C.

The class representing these descriptors is HBufC (explicitly HBufC8 or HBufC16) although these descriptors are always referred to by pointer, HBufC*. You’ll notice that, by starting with ”H”, the class doesn’t comply with the naming conventions described in Chapter 1. The class is exceptional and doesn’t really fit any of the standard Symbian OS types exactly. Thus, it is simply prefixed with H to indicate that the data is stored on the heap. If you’re interested in how the cleanup stack handles HBufC, see Chapter 3.

The HBufC class exports a number of static NewL() functions to create the buffer on the heap. These follow the two-phase construction model (described in Chapter 4) since they may leave if there is insufficient memory available. There are no public constructors and all heap buffers must be constructed using one of these methods (or from one of the Alloc() or AllocL() methods of the TDesC class which you may use to spawn an HBufC copy of any descriptor).

As you’ll note from the ”C” in the class name, these descriptors are nonmodifiable, although, in common with the stack-based non-modifiable buffer descriptors, the class provides a set of assignment operators to allow the entire contents of the buffer to be replaced. As with TBufC, the length of the replacing data must not exceed the length of the heap cell allocated for the buffer or a panic occurs.

In common with TBufC, the heap-based descriptors can be manipulated at runtime by creating a modifiable pointer descriptor, TPtr, using the Des() method.

_LIT(KPalindrome, "Do Geese see God?"); TBufC<20> stackBuf(KPalindrome);

// Allocates an empty heap descriptor of max length 20 HBufC* heapBuf = HBufC::NewLC(20);

TInt length = heapBuf->Length();// Current length = 0

TPtr ptr(heapBuf->Des());

//

Modification of the heap descriptor

ptr = stackBuf; // Copies stackBuf

contents into heapBuf

length =

heapBuf->Length();

// length = 17

HBufC* heapBuf2 = stackBuf.AllocLC(); // From stack buffer

length =

heapBuf2->Length();

 

// length = 17

_LIT(KPalindrome2, "Palindrome");

*heapBuf2 = KPalindrome2; // Copy and replace data in heapBuf2 length = heapBuf2->Length(); // length = 10 CleanupStack::PopAndDestroy(2, heapBuf);

Remember, the heap descriptors can be created dynamically to the size you require, but they are not automatically resized should you want

HEAP-BASED BUFFER DESCRIPTORS

67

to grow the buffer. You must ensure that the buffer has sufficient memory available for the modification operation you intend to use.

To help you with this, the HBufC class also defines a set of ReAllocL() methods to allow you to extend the heap buffer, which may potentially move the buffer from its previous location in memory (and may of course leave if there is insufficient memory). If the HBufC* is stored on the cleanup stack, moving the pointer as a result of memory reallocation can cause significant problems either in the event of a leave or if the cleanup stack’s PopAndDestroy() function is used to destroy the memory. If you call Des() on a heap descriptor to acquire a TPtr, the iPtr member of this object is not guaranteed to be valid after reallocation; it’s safest to assume that the buffer will have moved and create a new TPtr accordingly.

There is no modifiable heap descriptor, HBuf, which you may have expected in order to make heap buffers symmetrical with TBuf stack buffers. The reasons for this are manifold. First, the expectation might reasonably be that the maximum length of a modifiable HBuf class would expand and contract on the heap as the content was modified. The goal of descriptors is efficiency, with memory managed by the programmer not the descriptor object, but an HBuf class which is modifiable but does not dynamically resize may be considered somewhat odd. To add dynamic resizing to HBuf would be difficult and costly in terms of code size because, as I’ve described, all the modifiable descriptor operations are implemented in its base class, TDes. In addition, if dynamic reallocation were added to HBuf, the programmer would have to be made aware that the location of the buffer might change as code is reallocated, and that certain functions might fail if there is insufficient memory, probably requiring a new set of leaving functions to be added to the class.

Heap buffers were initially intended to allow efficient reading of constant resource strings of variable length. Being non-modifiable, they save on the additional four bytes required to store a maximum length, which allows them to be as compact as possible. The Des() method permits them to be modified if necessary, when the programmer makes an explicit choice to do so. Providing a separate HBuf class would require the programmer to decide whether the buffer would ever require modification. It is possible that many would opt to use HBuf rather than HBufC ”just in case” the descriptor later needed modification. Besides the additional code required for an HBuf class, the extra 4-byte overhead of HBuf objects created unnecessarily might add up over time. Providing the Des() method on the HBufC allows the programmer to modify heap buffers when needed but keeps the code associated with heap descriptor objects compact.

A counter-argument to this might be that the HBufC::Des() call is non-trivial and the resultant TPtr will occupy 12 bytes of stack space. In cases where a modifiable heap descriptor is definitely needed, creating a

68

DESCRIPTORS: SYMBIAN OS STRINGS

non-modifiable buffer and an additional, modifiable pointer to update it may be seen as a waste of processor instructions and memory. Perhaps the solution in future will be to provide both non-modifiable and modifiable heap buffers, with clear documentation as to the consequences of using each. With education, we should be able to trust developers to make the right choice of descriptor for the right operation, using constant heap descriptors by default and modifiable heap descriptors where necessary. I hope this book, and this chapter in particular, will go some way in helping to reach this goal!

To summarize, the inheritance hierarchy of the descriptor classes is shown in Figure 5.3.

TDesC

iLength iType Length() Ptr()

...constant descriptor methods

 

TPtrC

 

TBufCBase

 

 

TDes

 

 

 

 

 

 

 

 

 

 

iPtr

 

 

 

 

iMaxLength

 

 

 

 

 

 

 

MaxLength()

 

 

 

 

 

 

 

 

 

 

 

 

...modifiable descriptor

 

 

 

 

 

 

 

methods

 

 

 

 

 

 

 

 

 

TBufC<n>

 

 

HBufC

 

 

TPtr

 

 

TBufBase

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

iBuf

 

 

iBuf

 

 

iPtr

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

TBuf<n>

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

iBuf

 

Figure 5.3 Class inheritance hierarchies

 

 

 

 

 

 

 

 

 

 

Heap descriptors can be created dynamically to the size you require, but are not automatically resized should they need to be extended beyond their maximum length.