Symbian OS Explained - Effective C++ Programming For Smartphones (2005) (779885), страница 22
Текст из файла (страница 22)
Thesehave been optimized for the most efficient storage of descriptor data.90GOOD DESCRIPTOR STYLE• Use of stack-based objects of types TFileName and TParse shouldbe kept to a minimum because they reserve a large amount of stackspace (524 bytes), often unnecessarily.• Symbian OS provides the TLex classes for lexical analysis andextraction.• The package descriptor classes TPckgBuf, TPckg and TPckgC areuseful template classes for type-safe wrapping of the built-in types orT class objects within descriptors.
This method of ”descriptorizing”flat data is particularly useful for passing objects between client andserver, and is described in detail in Chapters 10, 11 and 12.7Dynamic Arrays and BuffersMake my skin into drumheads for the Bohemian causeThe last words of Czech General Jan Zizka (1358–1424)This chapter discusses the use of the dynamic array classesRArray<class T> and RPointerArray<class T>. It also describesthe CArrayX<class T> classes and the dynamic buffer classes theyuse. Dynamic array classes are very useful for manipulating collections ofdata without needing to know in advance how much memory to allocatefor their storage. They expand as elements are added to them and, unlikeC++ arrays, do not need to be created with a fixed size.Conceptually, the logical layout of an array is linear, like a vector.However, the implementation of the dynamic array can either use a singleheap cell as a ”flat” buffer to hold the array elements or allocate the arraybuffer in a number of segments, using a doubly-linked list to manage thesegmented heap memory.
Contiguous flat buffers are typically used whenhigh-speed pointer lookup is an important consideration and when arrayresizing is expected to be infrequent. Segmented buffers are preferablefor large amounts of data and where the array is expected to resize frequently, or where a number of elements may be inserted into or deletedfrom the array.The capacity of a dynamic array is the number of elements the arraycan hold within the space currently allocated to its buffer. When thecapacity is filled, the array dynamically resizes itself by reallocating heapmemory when the next element is added. The number of additionalelements allocated to the buffer is determined by the granularity, whichis specified at construction time.It is important to choose an array granularity consistent with theexpected usage pattern of the array. If too small a value is used, anoverhead will be incurred for multiple extra allocations when a largenumber of elements are added to the array.
However, if too large agranularity is chosen, the array will waste storage space.For example, if an array typically holds 8 to 10 objects, then agranularity of 10 would be sensible. A granularity of 100 would be92DYNAMIC ARRAYS AND BUFFERSunnecessary. However, if there are usually 11 objects, a granularity of 10wastes memory for 9 objects unnecessarily. A granularity of 1 would alsobe foolish, since it would incur multiple reallocations.Symbian OS provides a number of different dynamic array classeswith names prefixed by ”CArray”, such as CArrayFixFlat, CArrayFixSeg and CArrayVarSeg (which I’ll refer to collectively as”CArrayX”), as well as the RArray and RPointerArray classes. Itcan be quite difficult to determine which to use, so this chapter guidesyou through their main characteristics.Choose the granularity of a dynamic array carefully to avoid wastingstorage space (if the granularity chosen is too large) or frequentre-allocations (if it is chosen too small).7.1 CArrayX ClassesThere are a number of CArrayX classes, which makes this dynamicarray family very flexible, albeit with an associated performance overhead which I’ll discuss later in this chapter.
To sidestep the performancepenalty, the RArray and RPointerArray classes were added to Symbian OS to provide simpler and more efficient dynamic arrays. You shoulduse these classes in preference to CArrayX where possible.However, for background information, I’ll run through some briefdetails of the CArrayX classes. The naming scheme works as follows; foreach class the CArray prefix is followed by:• Fix for elements which have the same length and are copied so theymay be contained in the array buffer.• Var where the elements are of different lengths; each element iscontained within its own heap cell and the array buffer containspointers to the elements.• Pak for a packed array where the elements are of variable length; theyare copied so they may be contained within the array buffer. Eachelement is preceded by its length information, rather like a descriptor.• Ptr for an array of pointers to CBase-derived objects.Following this, the array class name ends with ”Flat”, for classeswhich use an underlying flat buffer for the dynamic memory of the array,or ”Seg”, for those that use a segmented buffer.
Figure 7.1 illustrates thevarious layouts available.CArrayX CLASSESFixVar or Ptr93Pak1256element lengthFlat BufferHeap Memory occupiedby a valid elementUnoccupied elementGranularity = 4Segmented BufferFigure 7.1 Memory layout of Symbian OS dynamic arraysAs I described above, the RArray classes are more efficient for simplearrays (flat arrays of fixed-length objects). For this reason, I will not discussthe CArrayFixFlat and CArrayPtrFlat classes at all, because youshould use the RArray classes in preference.However, there are other CArrayX classes which can be useful whenyou have variable-length elements or if you need to use a segmentedbuffer,1 because there are no directly analogous RArray classes:• CArrayVarFlat is used for variable-length elements referenced bypointer elements, using a flat memory layout for the array• CArrayVarSeg is used for variable-length elements referenced bypointer elements, using a segmented array layout• CArrayPakFlat is used for fixed- or variable-length elements thatare stored in the flat array buffer itself, each element containinginformation about its length1You may prefer to use a segmented buffer if reallocations are expected to be common,i.e.
if the size of the array is likely to change frequently. If a single flat buffer is used,numerous reallocations may result in heap thrashing and copying. In addition, insertionand deletion in a segmented buffer can be more efficient than in a flat buffer, since it doesnot require all the elements after the modification point to be shuffled. However, you mustweigh up the benefits of using a segmented memory buffer for the array against the otheroptimizations the flat RArray classes offer.94DYNAMIC ARRAYS AND BUFFERS• CArrayPtrSeg is used for an array of pointers in a segmented array.The inheritance hierarchy of the CArrayX classes is fairly straightforward. All of the classes are C classes and thus ultimately derivefrom CBase.
Each class is a thin template specialization of one of thearray base classes, CArrayVarBase, CArrayPakBase or CArrayFixBase. Thus, for example, CArrayVarSeg<class T> and CArrayVarFlat<class T> derive from CArrayVar<class T> which is atemplate specialization of CArrayVarBase, as shown in Figure 7.2.CArrayVarBase owns an object that derives from CBufBase, thedynamic buffer base class, and is used to store the elements of the array.The object is a concrete instance of CBufFlat (a flat dynamic storagebuffer) or CBufSeg (a segmented dynamic buffer).
I’ll discuss the dynamicbuffer classes in more detail later.CBase{abstract}CArrayVarBaseCBufBaseRead()Write()ExpandL()...Count()Length()...CBufBase* iBaseTCArrayVarCBufFlatAppendL()Operator[]......TUint8* iPtr...TDblQue<TBufSegLink>iQueTBufSegLink* iSegTTCArrayVarFlatCBufSegCArrayVarSegSee e32base.hfor further detailsFigure 7.2 Inheritance hierarchy of the variable-length element array classesHere is some example code showing how to manipulate the CArrayPtrSeg class. There’s quite a lot of code but don’t worry, it’s quiteCArrayX CLASSES95straightforward, and I’ll reuse it throughout the chapter to illustrate someof the other dynamic array classes. I’ve kept the sample code as brief aspossible by omitting error checking and other code which isn’t directlyrelevant to this chapter.The example is of a very basic task manager which can be used tostore tasks and execute them.
The task manager class, CTaskManager,owns a dynamic array of pointers (in a CArrayPtrSeg object) to heapbased objects of the task class, TTask. The example shows the use ofAppendL() and InsertL() to add an object to the array, Delete()to remove an element and At() and operator[] to access elements inthe array.The TTask class is rather empty, because I’ve implemented just theminimum amount of code for the example. You’ll notice that it is a Tclass (as described in Chapter 1) and that I create objects on the heapand transfer ownership to the task manager array. Of course, I could haveused any of the other CArrayX dynamic array classes to store the TTaskobjects, but I wanted to illustrate the use of the pointer array, particularlyon cleanup.
If the objects stored in the pointer array are not owned byanother object, it is the responsibility of the array to destroy them whenthey are removed from the array or when the array itself is destroyed.You’ll notice in the CTaskManager::Delete() method below that Istore a pointer to the TTask object to be deleted, remove it from thearray and then destroy it.
In the destructor for CTaskManager, I callResetAndDestroy() on the array, which empties the array, callingdelete on every pointer.class TTask // Basic T class, represents a task{public:TTask(const TDesC& aTaskName);void ExecuteTaskL() {}; // Omitted for clarityprivate:TBuf<10> iTaskName;};TTask::TTask(const TDesC& aTaskName){ iTaskName.Copy(aTaskName); }// Holds a dynamic array of TTask pointersclass CTaskManager : public CBase{public:static CTaskManager* NewLC();∼CTaskManager();public:void AddTaskL(TTask* aTask);void InsertTaskL(TTask* aTask, TInt aIndex);void RunAllTasksL();void DeleteTask(TInt aIndex);public:96DYNAMIC ARRAYS AND BUFFERSinline TInt Count(){return (iTaskArray->Count());};inline TTask* GetTask(TInt aIndex){return(iTaskArray->At(aIndex));};private:void ConstructL();CTaskManager() {};private:CArrayPtrSeg<TTask>* iTaskArray;};const TInt KTaskArrayGranularity = 5;CTaskManager* CTaskManager::NewLC(){CTaskManager* me = new (ELeave) CTaskManager();CleanupStack::PushL(me);me->ConstructL();return (me);}CTaskManager::∼CTaskManager(){// Cleanup the arrayif (iTaskArray)iTaskArray->ResetAndDestroy(); // Destroys objects through ptrsdelete iTaskArray;}void CTaskManager::ConstructL(){iTaskArray =new (ELeave) CArrayPtrSeg<TTask>(KTaskArrayGranularity);}void CTaskManager::AddTaskL(TTask* aTask){ // Add a task to the end of array// No need to check that aTask! =NULL because CBufBase does this// before appending itiTaskArray->AppendL(aTask);}void CTaskManager::InsertTaskL(TTask* aTask, TInt aIndex){ // Insert a task into a given element index// No assertion on aTask or aIndex because CArrayFixBase// and CBufBase do thisiTaskArray->InsertL(aIndex, aTask);}void CTaskManager::RunAllTasksL(){ // Iterates all TTask objects and calls ExecuteTaskL()TInt taskCount = iTaskArray->Count();for (TInt index = 0; index < taskCount; index++){(*iTaskArray)[index]->ExecuteTaskL();}}RArray<class T> AND RPointerArray<class T>97void CTaskManager::DeleteTask(TInt aIndex){ // Removes the pointer from the array// The function stores a pointer to it so it can be destroyedTTask* task = iTaskArray->At(aIndex);if (task){iTaskArray->Delete(aIndex); // Does not delete the objectdelete task; // Deletes the object}}// Calling codevoid TestTaskManagerL(){CTaskManager* taskManager = CTaskManager::NewLC();// Add four tasks to the array_LIT(KTaskName, "TASKX%u");for (TInt index =0; index<4; index++){TBuf<10> taskName;taskName.Format(KTaskName, index); // Names each taskTTask* task = new (ELeave) TTask(taskName);CleanupStack::PushL(task);taskManager->AddTaskL(task);CleanupStack::Pop(task); // Now owned by the taskManager array}ASSERT(4==taskManager->Count()); // Chapter 16 discusses ASSERTs// Insert a task into element 3_LIT(KNewTask, "InsertedTask");TTask* insertedTask = new (ELeave) TTask(KNewTask);CleanupStack::PushL(insertedTask);taskManager->InsertTaskL(insertedTask, 3);CleanupStack::Pop(insertedTask); // Now owned by taskManagerASSERT(5==taskManager->Count());// Delete a tasktaskManager->DeleteTask(2);ASSERT(4==taskManager->Count());taskManager->RunAllTasksL();// Destroys the array (which itself destroys the TTasks it owns)CleanupStack::PopAndDestroy(taskManager);}7.2 RArray<class T> and RPointerArray<class T>RArray and RPointerArray are R classes, the characteristics of whichwere described in Chapter 1.