×
Namespaces

Variants
Actions

Fundamentals of Symbian C++/Arrays

From Nokia Developer Wiki
Jump to: navigation, search
Article Metadata
Article
Created: User:Twm (30 Mar 2009)
Last edited: hamishwillee (12 Dec 2012)

Symbian C++ provides a large number of array classes, which should be used in preference to standard C/C++ arrays because of the protection they provide (against memory overruns and so on.). Otherwise they behave as standard C++ arrays (stl::vector<>); the arrays are indexed by an integer, elements can be of any type (or pointer to a type), the array does its internal reallocation when adding or deleting.

Contents

Array Overview

See the Symbian Developer Library for the complete description of these classes and their APIs. This section provides an introductory summary.

Array Family Classes Description
CArrayX CArrayFixFlat CArrayVarFlat CArrayPakFlat CArrayPtrFlat CArrayFixSeg CArrayVarSeg CArrayPtrSeg A collection of dynamic array classes.
RArrays RArray

RPointerArray

Dynamic array classes optimized for efficiency over CArrayX classes.
Fixed Array TFixedArray A fixed-length array class.
Descriptor Arrays CDesC16ArrayFlat CDesC8ArraySeg CDesC8ArrayFlat CDesC8ArraySeg CPtrC8Array CPtrC16Array A collection of dynamic array classes specialized for storing Fundamentals of Symbian C++/Descriptors.

Symbian C++ arrays are thin template classes (see Section Symbian C++ Miscellany).

CArrayX Classes

You can visualize the layout of an array as linear but, on Symbian C++, the implementation of a dynamic array may use either:

  • A single heap cell as a flat buffer to hold the array elements, which is resized as necessary. Flat buffers are preferred when the speed of element lookup is an important factor or if the array is not expected to have frequent item insertion/deletion.
  • A number of segments, connected using a doubly-linked list. Segmented buffers are best for large arrays that are expected to be resized frequently, or when elements are regularly inserted into or deleted.

There is a number of different types of dynamic array classes, all of which have names prefixed by CArray. We’ll refer to them here generically as CArrayX classes. The full naming scheme takes the CArray prefix and follows it with:

  • Fix where the elements of the array all have the same length and are copied directly into the array buffer (for example, TPoint, TRect)
  • Var where the elements of the array are pointers to objects of variable lengths contained elsewhere on the heap (such as HBufC* or TAny*)
  • Pak (for ‘packed’ array) where the elements of the array are of variable length but are copied into the array buffer with each preceded by its length (for example, T class objects of variable length)
  • Ptr where the elements of the array are pointers to CBase-derived objects.

The array class name ends with:

  • Flat for classes that use an underlying flat buffer layout
  • Seg for those that use a segmented buffer.

The figure below illustrates the various memory layouts available, and the table summarizes each of the classes and how to destroy the elements it contains at cleanup time.

Memory layout of Symbian C++ dynamic arrays


Class name Elements and memory layout Element cleanup
CArrayFixFlat Fixed-size elements contained in the array.

Occupies a single memory buffer that is resized as necessary.

Destroyed automatically by the array.
CArrayFixSeg Fixed-size elements contained in the array.

Occupies segments of memory.

Destroyed automatically by the array.
CArrayVarFlat Elements are pointers to variable size objects contained outside the array.

Occupies a single memory buffer that is resized as necessary.

Destroyed automatically by the array.
CArrayVarSeg Elements are pointers to variable-size objects contained outside the array.

Occupies segments of memory.

Destroyed automatically by the array.
CArrayPtrFlat Elements are pointers to CBase-derived objects stored outside the array.

Occupies a single memory buffer that is resized as necessary.

Elements must be destroyed by calling ResetAndDestroy() on the array before destroying it.
CArrayPtrSeg Elements are pointers to CBase-derived objects stored outside the array.

Occupies segments of memory.

Elements must be destroyed by calling ResetAndDestroy() on the array before destroying it.
CArrayPakFlat Variable size elements occupy the array, each preceded by its length.

Occupies a single memory buffer that is resized as necessary.

Destroyed automatically by the array.

CArrayX arrays are created using new(ELeave) as usual for CBase-derived objects, with the type of array element and granularity (see the later section for a discussion on granularity) specified as in the following example:

const TUint KArrayGranularity = 32;
 
CArrayFixFlat* array = new(ELeave) CArrayFixFlat<TMyType>(KArrayGranularity);
 
CleanupStack::PushL(array);
 
Array->InsertL(myType);
 
CleanupStack::PopAndDestroy();

Note that CArrayPtrXXX arrays are not automatically assumed to own their elements; these are therefore not automatically destroyed with the array. In order to destroy both the array and its elements, it is necessary to call ResetAndDestroy() prior to deletion of the array. This assumption is true of all the pointer array types, including RPointerArray and the descriptor pointer arrays.

RArrays

RArray and RPointerArray are dynamic arrays, where the array buffer is stored in a resizable flat buffer. As R classes, they hold a handle to a block of memory used to store the array buffer, which in both cases is a resizable flat buffer rather than a doubly linked list of segments.

RArray and RPointerArray have significantly better performance than the CArrayX classes. Note that the RArrays cannot be used to store arrays of bytes or 16-bit words (see Symbian C++ Knowledgebase Q&As on this topic).

RArray<class T> is an array of elements of the same size that are stored within the array buffer. The Close() or Reset() functions must be called to clean up the array by freeing the memory allocated for the elements.

  • RArray::Close() frees the memory used to store the array and closes the array object.
  • RArray::Reset() frees the memory associated used to store the array and resets its state so it can be re-used.

An example declaration of an RArray is as follows:

RArray<MyObj> myArray;

This will define a dynamic array myArray that contains objects of type MyObj. Note that this is similar to TFixedArray (discussed below) except an array size is not specified with the template. This is because the size is not pre-allocated as it is for TFixedArray.

RPointerArray<class T> is an array of pointer elements addressing objects stored elsewhere. The ownership of these objects must be addressed separately when the array is destroyed. If pointers to the objects are held elsewhere (by other components) then calling Close() or Reset() to clean up the memory for the array of pointers is sufficient. However, if the array has ownership of the objects its elements point to, it must take responsibility for cleanup. ResetAndDestroy() must be called to delete the object associated with each pointer element in the array, and then destroy the array itself.

RPointerArray is like RArray except that pointers to the objects are held in the array instead of copies of the objects. When using these, ensure that you do not delete the objects after they are added to the array.

RPointerArray is declared as follows:

RPointerArray<MyObj> myArray;

This is like RArray, except that in this case an array of pointers to type MyObj is stored.

An in-depth discussion of RArrays can be found at: Advanced RArray

TFixedArray

The TFixedArray class can be used when the number of elements it will contain is known at compile time. It wraps a standard fixed-length C++ [] array to add range checking, and prevents out-of-bounds access by panicking to flag the programming error. The range-checking assertions are called in debug builds only, or in both debug and release builds, if you require it, by calling different methods on the class to access the elements. The class also provides some additional functions to navigate to the beginning and end, return the number of elements in the array, and cleanup the elements it contains.

A fixed array is declared as follows:

TFixedArray<type, size> myArray;

This line will allocate a fixed array called myArray where type represents the data type of the data in the array and size indicates the maximum number of objects the array is allocated for. A TFixedArray may be used in a similar way to an array in C:

TFixedArray<TInt, 10> array;
 
for (TInt ii = 0; ii < 10; ii++)
array[ii] = ii;
 
array[10] = 10; // Generates an exception, outside of array bounds.

Fixed arrays are a lightweight and efficient way of implementing an array while providing access range checking. Like traditional arrays, the number of items in the array is pre-allocated at compile time.

Descriptor Arrays

Descriptor array classes implement arrays of TDesC-based buffer descriptors. The purpose of these classes, in most cases, is to implement an array of strings. For example, a list box keeps its list of selection item strings in a descriptor array.

Descriptor arrays use dynamic buffers to store their data. Therefore, the array size does not need to be pre-allocated, as it is for a fixed array. The array is expanded as needed when new data items are added to it.

Descriptor arrays can be flat or segmented, contain 8- or 16-bit descriptors, and contain either copies of the descriptors (in HBufC’s) or pointers to descriptors (in TPtr’s).

Here are the instantiable descriptor array classes:

  • CDesC16ArrayFlat: An array of 16-bit descriptors stored in a flat dynamic buffer
  • CDesC16ArraySeg: Same as CDesC16ArrayFlat, but data is stored in a segmented dynamic buffer
  • CDesC8ArrayFlat: An array of 8-bit descriptors stored in a flat dynamic buffer
  • CDesC8ArraySeg: Same as CDesC8ArrayFlat, but data is stored in a segmented dynamic buffer
  • CDesCArrayFlat: Same as CDesC16ArrayFlat for the standard Unicode build
  • CDesCArraySeg: Same as CDesC16ArraySeg for the standard Unicode build
  • CPtrC16Array: Array of TPtrC objects that point to the descriptor data elements
  • CPtrC8Array: Same as CPtrC16Array, but stores 8-bit descriptors.
  • CPtrCArray: Equivalent to CPtrC16Array for the standard Unicode build.

Descriptor array classes that end in Flat and Seg indicate the flat and segmented type of dynamic buffer used to store the array’s data. Refer to the previous section on dynamic buffers for more information. Use a flat array if the array is not expanded very often, otherwise use a segmented array.

Note that the classes that begin in CPtrC use flat buffers only – no segmented versions of these classes are supplied.

Descriptor array classes that begin with CDesC are implemented as an array of HBufC pointers. When a descriptor is added to a CDesC array, the array class will allocate a new HBufC, copy the data from the descriptor to this HBufC, and, finally, write the HBufC’s pointer to the appropriate position in the array. For example:

_LIT(KString1, "My String");
 
_LIT(KString2, "Test One Two");
 
TBufC<20> myDes(KString1);
 
TBuf<20> myDes1(KString2);
 
CDesCArrayFlat myArray = new (ELeave) CDesCArrayFlat(5);
 
CleanupStack::PushL(myArray); // in case the appends leave
 
myArray->AppendL(myDes);
 
myArray->AppendL(myDes1);
 
/* ... */
 
CleanupStack::PopAndDestroy(myArray);

Since copies of the descriptors are made and referenced in the array, it does not matter if the user deletes the objects after they are added to the array. Of course, the disadvantage of using this type of array is that you have overheads in both performance (doing the copy) and memory (duplicating descriptor data in memory).

An array class that begins with CPtrC contains descriptor pointers (TPtrC’s) as its elements.

Unlike the CDesC array classes, CPtrC classes do not have to copy or store the data in the descriptors that are added to the array, since they simply point to the descriptor data. However, you need to make sure that you do not add any descriptors to the array that may go out of scope or be otherwise deleted, since the array would contain a TPtrC pointing to an undefined area.

To use the descriptor arrays, you need to include file badesca.h and link to library bafl.lib.

Granularity and Capacity

All the Symbian C++ dynamic arrays have the concept of size, capacity and granularity. This knowledge may help for debugging and understanding performance aspects, it does not affect the semantics.

The size of an array is the number of elements the arrays holds now - it is incremented whenever you add an element. The capacity of an array is the number of elements the array can hold within the space currently allocated to its internal buffer; capacity is always greater than or equal to size. When the buffer is full (size==capacity), the next insertion will cause the array to resize itself, by re-allocating heap memory. The amount of additional space it reserves is set at construction time and is called the granularity.

The granularity should be chosen carefully according to the way the array will be used. If it is too small, this means the array must make frequent re-allocation, which is inefficient. A common error is to set the granularity to one, which means that every addition to the array causes the underlying memory buffer to be re-allocated. This is very inefficient and can cause heap fragmentation. However, on the other side of the scale, if an array granularity is set too large, the array will waste memory. For example, if the array only ever contains between five and ten items, the granularity should not be set to 20. Note that once an array have been expanded, space is not reclaimed automatically. To achieve this, it needs to be compressed.

Which Dynamic Array Class To Use

Comparing like with like (RArray with CArrayFixFlat, and RPointerArray with CArrayPtrFlat), the RArray and RPointerArray classes have significantly better performance than CArrayX classes. Let’s consider the reasons for this.

  • The base class to all the CArrayX classes requires a TPtr8 descriptor object to be constructed for every array access, which has a performance overhead, even for a simple flat array containing fixed-length elements.
  • Every method that accesses the array has a minimum of two assertions in release and debug builds (through use of the __ASSERT_ALWAYS macro described in Fundamentals of Symbian C++/Symbian C++ Miscellany).
  • A number of the array-manipulation functions of CArrayX, such as AppendL(), can leave, for example when there is insufficient memory to resize the array buffer. In some cases, the array must be called within a function that cannot leave, which means that these functions must be called in a TRAP macro. As Fundamentals of Symbian C++/Leaves & The Cleanup Stack describes, a TRAP macro has an associated performance overhead.

The R classes do not require descriptors to be constructed for array access, do not use release build assertion checking and provide both leaving and non-leaving methods (the non-leaving methods may still fail, so the error value they return must always be checked).

For these reasons, the R classes are preferable as efficient and simple flat-memory arrays. The CArrayX classes have been retained for legacy code, and because they provide a segmented-memory implementation, which may be more appropriate for arrays that are frequently resized. Thus, CArrayFixSeg and CArrayPtrSeg are useful alternatives to RArray and RPointerArray, respectively when segmented memory is required, but RArray should be preferred over CArrayFixFlat, and RPointerArray over CArrayPtrFlat.

The examples given illustrate the use of both types of array for completeness.

Sorting and Ordering

To sort and search the CArrayX classes, an array key is used to define a property of an array. For example, for an array of elements, which are types encapsulating an integer and a string, a key based on the integer value may be used to sort the array. Alternatively, a key may be used to search for an element containing a particular string or sub-string.

The abstract base class for the array key is TKey. The following TKey-derived classes implement keys for different types of array:

  • TKeyArrayFix for arrays with fixed-length elements
  • TKeyArrayVar for arrays with variable-length elements
  • TKeyArrayPak for arrays with packed elements.

The appropriate TKeyArrayFix, TKeyArrayVar or TKeyArrayPak object is constructed and passed to the Sort(), InsertIsqL(), Find() or FindIsq() array-class member function. A search can be sequential, using Find(), or binary-chop, using FindIsq(). Both functions indicate the success or failure of the search and the position of the element within the array if it is found.

The RArray and RPointerArray classes also provide searching and ordering. Ordering is performed using a comparator function which must be implemented by the class making up the elements themselves, or a function which can access the relevant member data of the elements. The ordering method is passed to the InsertInOrder() or Sort() methods, by wrapping it in a TLinearOrder<class T> package.

Search operations on the RArray and RPointerArray classes are similar. The classes have several Find() methods that take a method, usually provided by the element class, that determines whether an element objects matches the item to be found.


Licence icon cc-by-sa 3.0-88x31.png© 2010 Symbian Foundation Limited. This document is licensed under the Creative Commons Attribution-Share Alike 2.0 license. See http://creativecommons.org/licenses/by-sa/2.0/legalcode for the full terms of the license.
Note that this content was originally hosted on the Symbian Foundation developer wiki.

This page was last modified on 12 December 2012, at 01:05.
56 page views in the last 30 days.

Was this page helpful?

Your feedback about this content is important. Let us know what you think.

 

Thank you!

We appreciate your feedback.

×