quick_recipes (779892), страница 8
Текст из файла (страница 8)
This means that, if youare mixing C++ exceptions in with traditional Symbian OS leaves andTRAPs, you must not throw an exception within a leaving function unlessyou also catch and handle it internally in that function.A standard catch block will not manage the Symbian OS cleanupstack correctly either, so code that throws exceptions should not usethe cleanup stack directly or indirectly.
If you are calling leavingfunctions from exception-style code, do not use the cleanup stack,but make the calls as required and catch and handle any exceptions that arise. For more information about mixing leaves, exceptions and the cleanup stack, please refer to a paper on the Symbian Developer Network called ‘Leaves and Exceptions’ to located atdeveloper.symbian.com/main/downloads/papers/Leaves%20and%20Exceptions.pdf.3.6 Two-Phase ConstructionConsider the following code, which allocates an object of type CExampleon the heap and sets the value of foo accordingly:CExample* foo = new(ELeave) CExample();The code calls the new operator, which allocates a CExample objecton the heap if there is sufficient memory available.
Having done so, itcalls the constructor of class CExample to initialize the object. But ifthe CExample constructor leaves, the memory already allocated for fooand any additional memory the constructor may have allocated will beorphaned.This leads to a fundamental rule of memory management on Symbian OS: A C++ constructor should never leave.However, it may often be necessary to write initialization code thatleaves, say to allocate memory to store another object, or to read froma configuration file, which may be missing or corrupt.
There are manyreasons why initialization may fail, and the way to accommodate this onSymbian OS is to use two-phase construction.Two-phase construction breaks object construction into two parts, orphases:1.A C++ constructor that cannot leave.This is the constructor that is called by the new operator. It implicitlycalls base-class constructors and may also invoke functions thatTWO-PHASE CONSTRUCTION41cannot leave, and/or initialize member variables with default valuesor those supplied as arguments to the constructor.2. A separate initialization method (typically called ConstructL()).This method may be called separately once the object, allocated andconstructed by the new operator, has been pushed onto the cleanupstack; it will complete construction of the object and may safelyperform operations that may leave.
If a leave does occur, the cleanupstack calls the destructor to free any resources which have alreadybeen successfully allocated and destroys the memory allocated forthe object itself.A class typically provides a public static function which wraps bothphases of construction, providing a simple and easily identifiable meansto instantiate it (the two construction methods can then be made privateor protected to avoid accidental usage). The factory function is typicallycalled NewL() and is static so that it can be called without first havingan existing instance of the class.
For example:class CExample : public CBase{public:static CExample* NewL();static CExample* NewLC();∼CExample(); // Must cope with partially constructed objects... // Other public methods, e.g. Foo(), Bar()private:CExample();// Guaranteed not to leavevoid ConstructL(); // Second-phase construction code, may leaveCPointer* iPointer;};Note that there is also a NewLC() function in class CExample. Thismethod is also a factory function, but its implementation leaves a pointeron the cleanup stack when it returns.Tip: If a pointer to an object is pushed onto the cleanup stack andremains on it when that function returns, the Symbian OS conventionis to append a C to the function name.
This indicates to the caller that,if the function returns successfully, the cleanup stack has additionalpointers on it.Let’s see the code for typical implementations of NewL() andNewLC():CExample* CExample::NewLC(){42SYMBIAN OS DEVELOPMENT BASICSCExample* me = new (ELeave) CExample(); // First-phase constructionCleanupStack::PushL(me);me->ConstructL(); // Second-phase constructionreturn (me);}CExample* CExample::NewL(){CExample* me = CExample::NewLC();CleanupStack::Pop(me);return (me);}The NewL() factory function is implemented in terms of theNewLC() function rather than the other way around (which wouldbe slightly less efficient since this would require an extra PushL() callto put a pointer back onto the cleanup stack).Each factory function returns a fully constructed object, or leaves,either if there is insufficient memory to allocate the object (that is, ifoperator new(ELeave) leaves) or if the second-phase ConstructL()function leaves for any reason.
This means that, if an object is initializedentirely by two-phase construction, the class can be implemented withoutthe need to test each member variable to see if it is valid before usingit. That is, if an object exists, it has been fully constructed. The resultis an efficient class implementation without a test against each memberpointer before it is de-referenced.3.7 Thin TemplatesSymbian OS uses the thin template pattern in all its container classes,collections and buffers, to get the benefits of type-safety, while reducingthe amount of duplicated object code at compile time.The thin template idiom implements the container using a genericbase class with TAny* pointers.
A templated class is then defined thatuses private inheritance of the generic implementation. (If your C++ is abit rusty, private inheritance allows implementation to be inherited, butthe methods of the base class become private members. The derivingclass does not inherit the interface of the base class so the genericimplementation can only be used internally and not externally, whichavoids accidental use of the non-type-safe methods.) Instead of a genericinterface, the derived class presents a templated interface to its clientsand implements it inline by calling the private base-class methods. Theuse of templates means that the class can be used with any type requiredand that it is type-safe at compile time.THIN TEMPLATES43Let’s look at an example to make it clearer.
A snippet of the Symbian OSRArrayBase class and its deriving class RArray are shown below (theRArray class will be discussed later in Section 3.9.2). RArrayBase isthe generic base class that implements the code logic for the array, but thecode cannot be used directly because all its methods are protected.class RArrayBase{protected:IMPORT_C RArrayBase(TInt aEntrySize);IMPORT_C RArrayBase(TInt aEntrySize,TAny* aEntries, TInt aCount);IMPORT_C TAny* At(TInt aIndex) const;IMPORT_C TInt Append(const TAny* aEntry);IMPORT_C TInt Insert(const TAny* aEntry, TInt aPos);...};The derived RArray class is templated, defining a clear, usable API forclients.
The API is defined inline and uses the base-class implementationwhich it privately inherits from the base class. Elements of the array areinstances of the template class.template <class T>class RArray : private RArrayBase{public:...inline RArray();inline const T& operator[](TInt aIndex) const;inline T& operator[](TInt aIndex);inline TInt Append(const T& aEntry);inline TInt Insert(const T& aEntry, TInt aPos);...};template <class T>inline RArray<T>::RArray(): RArrayBase(sizeof(T)){}template <class T>inline const T& RArray<T>::operator[](TInt aIndex) const{return *(const T*)At(aIndex); }template <class T>inline T& RArray<T>::operator[](TInt aIndex){return *(T*)At(aIndex); }template <class T>inline TInt RArray<T>::Append(const T& aEntry){return RArrayBase::Append(&aEntry);}44SYMBIAN OS DEVELOPMENT BASICStemplate <class T>inline TInt RArray<T>::Insert(const T& aEntry, TInt aPos){return RArrayBase::Insert(&aEntry,aPos);}3.8 Descriptors – Symbian OS StringsDescriptors are the string classes used on Symbian OS.
They are so calledbecause they are self-describing strings. That is, a descriptor holds thelength of the string of data it represents as well as type information toidentify the underlying memory layout of the descriptor data. Descriptorsprotect against buffer overrun and don’t rely on NULL terminators todetermine the length of the string.Descriptors are used throughout the operating system, from the verylowest level upwards, and are designed to be very efficient, using theminimum amount of memory necessary to store data, while describingit fully in terms of its length and location.
Descriptors are strings andcan contain text data. However, they can also be used to manipulatebinary data, because they don’t rely on a NULL-terminating character todetermine their length.There are probably as many string classes as there are programmingplatforms, and everyone has their favorite. Many developers find SymbianOS descriptors hard to get used to at first, because there are many differentclasses to become familiar with. They can also be confusing because theyaren’t a direct analog to standard C++ strings, Java strings or the MFCCString (to take just three examples), since their underlying memoryallocation and cleanup must be managed by the programmer.
Beforecalling a modifiable descriptor method that may expand the descriptor,it is necessary to ensure that there is sufficient memory available for it tosucceed.3.8.1 Character SizeSymbian OS supports 8-bit and 16-bit character sizes. The character sizeof a descriptor class can be identified from its name. If the class nameends in 8 (for example, TPtr8) it has narrow (8-bit) characters, while adescriptor class name ending with 16 (for example, TPtr16) manipulates16-bit character strings.There is also a set of neutral classes which have no number in theirname (for example, TPtr). The neutral classes are 16 bit, and it is goodpractice to use the neutral descriptor classes where the character widthdoes not need to be stated explicitly.