Symbian OS Explained - Effective C++ Programming For Smartphones (2005) (779885), страница 63
Текст из файла (страница 63)
Thetype-unsafe base class (RArrayBase) implements the code logic for thearray but cannot be used directly because all its methods are protected.You’ll find a detailed discussion of the RArray class, and other SymbianOS container classes, in Chapter 7.1You will typically want to use templates when writing a class that manipulates severaldifferent types using the same generic code. The code should be agnostic about the typepassed into the template parameter, that is, the underlying logic is independent of type.Typical examples of (thin) template classes in Symbian OS are the array classes (I’ll discussRArray shortly), the singly- and doubly-linked list classes (based on TSglQueBase andTDblQueBase) and the circular buffers (based on CCirBufBase).2Private inheritance means that the derived class is implemented in terms of the baseclass. Private inheritance is used when the deriving class uses some of the implementedmethods of the base class, but has no direct conceptual relationship with the base class.Using private inheritance allows implementation to be inherited but all the methods of thebase class become private members of the deriving class.
In effect, the deriving class doesnot inherit the interface of the base class.THIN TEMPLATES295class RArrayBase{protected:IMPORT_C RArrayBase(TInt anEntrySize);IMPORT_C RArrayBase(TInt aEntrySize,TAny* aEntries, TInt aCount);IMPORT_C TAny* At(TInt anIndex) const;IMPORT_C TInt Append(const TAny* anEntry);IMPORT_C TInt Insert(const TAny* anEntry, TInt aPos);...};The templated RArray class privately inherits the implementation anddefines a clear, usable API for clients. The API is defined inline and usesthe base class implementation.
Elements of the array are instances of thetemplate class.template <class T>class RArray : private RArrayBase{public:...inline RArray();inline const T& operator[](TInt anIndex) const;inline T& operator[](TInt anIndex);inline TInt Append(const T& anEntry);inline TInt Insert(const T& anEntry, TInt aPos);...};template <class T>inline RArray<T>::RArray(): RArrayBase(sizeof(T)){}template <class T>inline const T& RArray<T>::operator[](TInt anIndex) const{return *(const T*)At(anIndex); }template <class T>inline T& RArray<T>::operator[](TInt anIndex){return *(T*)At(anIndex); }template <class T>inline TInt RArray<T>::Append(const T& anEntry){return RArrayBase::Append(&anEntry);}template <class T>inline TInt RArray<T>::Insert(const T& anEntry, TInt aPos){return RArrayBase::Insert(&anEntry,aPos);}Use of the class is then straightforward:void TestRArray(){const TInt arraySize = 3;RArray<TInt> myArray(arraySize);for (TInt index = 0; index<arraySize; index++){296THIN TEMPLATESmyArray.Append(index);}TInt count = myArray.Count();ASSERT(arraySize==count);for (index = 0; index<arraySize; index++){ASSERT(myArray[index]==index);}}For another example of the thin template pattern in Symbian OS,consider the TBufC and TBuf descriptor classes discussed in Chapter 5.These classes are templated on an integer, the value of which is used as thevariable which determines the maximum statically-allocated length of thebuffer.
In this case, the inheritance model is public and the derived classpublicly inherits the base class implementation (whereas the previousexample used private inheritance to gain access to the implementationwithout inheriting the behavior).The constructors of the derived TBufC16 class, and other functionsthat use the template parameter, are declared inline. I’ve shown theconstructors for the base and derived classes below (from e32des16.hand e32std.inl):class TBufCBase16 : public TDesC16{protected:IMPORT_C TBufCBase16();inline TBufCBase16(TInt aLength);IMPORT_C TBufCBase16(const TUint16 *aString,TInt aMaxLength);IMPORT_C TBufCBase16(const TDesC16 &aDes,TInt aMaxLength);...};template <TInt S>class TBufC16 : public TBufCBase16{public:inline TBufC16();inline TBufC16(const TUint16 *aString);inline TBufC16(const TDesC16 &aDes);...};template <TInt S>inline TBufC16<S>::TBufC16(): TBufCBase16(){}template <TInt S>inline TBufC16<S>::TBufC16(const TUint16 *aString): TBufCBase16(aString,S){}template <TInt S>inline TBufC16<S>::TBufC16(const TDesC16 &aDes)SUMMARY297: TBufCBase16(aDes,S){}To get the benefits of C++ templates without the disadvantages ofcode bloat, you should prefer the thin-template idiom which is usedthroughout Symbian OS code.19.1 SummaryThis chapter explained why C++ templates are ideal for reusable typesafe but type-agnostic code, but have the disadvantage that they canincrease their clients’ code size quite considerably.
For each templatedclass, every time a different type is used, separate code is generated forevery templated function. Furthermore, this code duplication occurs ineach client DLL or compilation unit using the templated class. This cancause significant code bloat unless the number of different types that areused with the templated class is limited, the code generated is small, andit is guaranteed that only a few clients will use the templated class.Many Symbian OS container classes use the thin template patternto gain the advantages of C++ templates without the disadvantages ofincreased code size (which is unacceptable on the ”small footprint”devices on which Symbian OS is deployed).This chapter described the characteristics of the thin template pattern,which typically defines a base class (containing a generic implementation,usually specified as protected to prevent it being called directly) and aderived class (which uses private inheritance to inherit the implementationof the base class).
The derived class exposes a templated interface,implemented inline in terms of the base class and thus benefits fromcompile-time type-checking by using C++ templates. The derived classdoes not have the associated size overhead because, regardless of thenumber of types used, the code is inline and uses a single copy of thegeneric base class implementation.In this chapter the RArray, TBuf and TBufC classes illustrated thethin template idiom as it is employed on Symbian OS, and how theseclasses should be used. The RArray class is discussed in detail inChapter 7, while TBuf and TBufC are examined in Chapter 5.20Expose a Comprehensive andComprehensible APIHave no fear of perfection – you’ll never reach itSalvador DaliSo, you’re ready to design a class.
You’ve read Chapter 1, and havethought about the characteristics of your class, maybe by asking thefollowing questions: ”How will objects of my class be instantiated?”;”Will they be stack- or heap-based?”; ”Will the class have any data orsimply provide an interface?”; ”Will it need a destructor?” The answersto these questions will help you decide what kind of Symbian OSclass it is and give you the first letter of its name, the rest of whichis down to you. When choosing a class name it is frequently difficultto be descriptive yet brief.
Unless your class consists solely of staticfunctions, it will be instantiated and used as an object. The class nameshould be a noun to reflect that. I’ll discuss the best strategy for namingclasses and their member data, methods and parameters, later in thischapter.The type of class you choose affects, to some extent, the definitionof the class.
For example, if you decide to implement it as a T class,it won’t have a destructor nor own any data which needs one. On theother hand, if you’re writing a C class, you’ll most likely need to usetwo-phase construction. To do this, you will make constructors protectedor private and provide a public static function, usually called NewL(),which calls a non-public second-phase construction method to performany initialization that may leave. You’ll find more information abouttwo-phase construction in Chapter 4.This chapter aims to highlight the factors you need to consider whendesigning a class, in particular the application programming interface(API) it will expose to its clients.
The chapter isn’t a comprehensivetreatise in class design – that would need a whole book – but it doespoint out some of the more important points for good C++ class designon Symbian OS.300EXPOSE A COMPREHENSIVE AND COMPREHENSIBLE APIThe quality of your API is important if your clients are to find it easyto understand and use. It should provide all the methods they need toperform the tasks for which the class is designed, but no more. Youshould design your interface to be as simple as possible, each memberfunction having a distinct purpose and no two functions overlapping inwhat they perform. Try and limit the number of methods, too – if yourclass provides too many it can be confusing and difficult to work with.A powerful class definition has a set of functions where each has a clearpurpose, making it straightforward, intuitive and attractive to reuse.
Thealternative might make a client think twice about using the class – if thefunctions are poorly declared, why trust the implementation? Besides thebenefit of avoiding duplicating the implementation effort, with limitedmemory space on a typical Symbian OS phone, it’s in everyone’s intereststo reuse code where possible.Even if your own code is the intended client, no matter! Should youdecide later to reuse the class, the time you spend making it convenientto use will pay off. By making the class simple to understand anduse, and cutting out duplicated functionality, you’ll cut down your testand maintenance time too. Furthermore, a well-defined class makesdocumentation easier and that’s got to be a good thing.20.1Class LayoutBefore looking at details of good API design, let’s consider the aestheticsof your class definition. When defining a class, make it easy for yourclients to find the information they need.
The convention is to lay outSymbian OS classes with public methods at the top, then protected andprivate methods, following this with public data if there is any (and laterin this chapter I’ll describe why there usually shouldn’t be), protectedand private data. I tend to use the public, protected and privateaccess specifiers more than once to split the class into logically relatedmethods or data. It doesn’t have any effect on the compiled C++, and itmakes the class definition simpler to navigate. For example:class CMyExample : public CSomeBase{public: // Object instantiation and destructionstatic CMyExample* NewL();static CMyExample* NewLC();∼CMyExample();public:void PublicFunc1();// Public functions, non virtual...public:inline TBool Inline1();// Inline (defined elsewhere)...public:IMPORT C AND EXPORT C−−301virtual void VirtualFunc1(); // Public virtual...protected:// Implementation of pure virtual base class functionsvoid DoProcessL();...// etc for other protected and private functionsprivate: // Construction methods, private for 2-phase constructvoid ConstructL();CMyExample();...private:// Data can also be grouped, e.g.
into that ownedCPointer* iData1; // by the class (which must be freed in the...// destructor) & that which does not need cleanup};20.2 IMPORT− C and EXPORT− CHaving declared your class, you should consider how your clients getaccess to it. If it will be used by code running in a separate executable (thatis, DLL or EXE code compiled into a separate binary component) fromthe one in which you will deliver the class, you need to export functionsfrom it. This makes the API ”public” to other modules by creating a .libfile, which contains the export table to be linked against by client code.There’s more about statically linked DLLs in Chapter 13.Every function you wish to export should be marked in the classdefinition in the header file with IMPORT_C.