Symbian OS Explained - Effective C++ Programming For Smartphones (2005) (779885), страница 4
Текст из файла (страница 4)
For historical reasons TBoolis equivalent to int and you should use the Symbian OS typedef’dvalues of ETrue (=1) and EFalse (=0). Bear in mind that C++ willinterpret any nonzero value as true. For this reason, you should refrainfrom making direct comparisons with ETrue.• Each TBool represents 32 bits, which may be quite wasteful ofmemory in classes with a number of flags representing state or settings.1Note that these are typedefs and should not be confused with the Symbian OSTRealX class, which describes an extended 64-bit precision real value.T CLASSES3You may wish to use a bitfield rather than a number of booleans,given that the 32 bits of a single TBool could hold 32 booleanvalues in a bitfield.
Of course, you should consider the potential codecomplexity, and trade that off against the benefits of a smaller object.The typedef’d set of Symbian OS built-in types are guaranteed tobe compiler-independent and should be used instead of the nativetypes except when returning void which is equivalent to ”nothing”.1.2 T ClassesT classes behave much like the C++ built-in types, hence they are prefixedwith the same letter as the typedefs described above (the ”T” is for”Type”). Like built-in types they have no destructor and, consequently, Tclasses should not contain any member data which itself has a destructor.Thus, a T class will contain member data which is either:• ”plain ol’ data” (built-in types) and objects of other T classes• pointers and references with a ”uses a” relationship rather than a ”hasa” relationship, which implies ownership.
A good example of this isthe TPtrC descriptor class, described in Chapter 5.T classes contain all their data internally and have no pointers, references or handles to data they own (although references to data ownedby other objects is allowed). The reason for not allowing ownership ofexternal data is because the T class must not have a destructor.Without a destructor, an object of a T class can be created on thestack and will be cleaned up correctly when the scope of that functionexits, either through a normal return or a leave (”leaving” is discussed indetail in Chapter 2).
If a T class were to have a destructor, Symbian OSwould not call it in the event of a leave because leaves do not emulatethe standard C++ throw semantics. If a call to the destructor werenecessary for the object to be safely cleaned up, the object could only becreated on the stack in the scope of code which can be guaranteed notto leave – which is clearly somewhat restrictive.An object of a T class can also be created on the heap.
Such anobject should be pushed onto the cleanup stack prior to calling codewith the potential to leave. In the event of a leave, the memory for the Tobject is deallocated by the cleanup stack (which is discussed in detail inChapter 3) but no destructor call is made.4CLASS NAME CONVENTIONS ON SYMBIAN OST classes are also often defined without default constructors; indeed,if a T class consists only of built-in types, a constructor would preventmember initialization as follows:TMyPODClass local = {2000, 2001, 2003};However, in the rare case that the T class has exported virtual functions,a default constructor must be exported because it is required for any clientcode to link against. The reasons for this are discussed in Chapter 20,which explains the EXPORT_C syntax.As a rule, the members of a T class will be simple enough to be bitwisecopied, so copy constructors and assignment operators are trivial andthe implicit compiler-generated versions are likely to be more efficient.So you generally don’t need to write a copy constructor or assignmentoperator unless, of course, you want to prevent cloning, in which caseyou should declare them both private to your class and leave themunimplemented.Some T classes have fairly complex APIs, such as TLex, for lexicalanalysis, and the descriptor base classes TDesC and TDes, described inChapter 5.
In other cases, a T class is simply a C-style struct consistingonly of public data (traditionally, a struct was prefixed with S instead ofT, but more recent Symbian OS code tends to make these T classes too).You’ll find the T prefix used for enumerations too, since these aresimple types. For example:enum TMonthsOfYear {EJanuary = 1, EFebruary = 2, ..., EDecember = 12};A T class must not have a destructor.1.3 C ClassesClasses with the C prefix2 ultimately derive from class CBase (defined ine32base.h). This class has two characteristics which are inherited byits subtypes and thus guaranteed for every C class.Firstly, CBase has a virtual destructor so a CBase-derived objectmay be destroyed properly by deletion through a CBase pointer.
This2In case you are wondering, the ”C” stands for ”Class”, which perhaps makes a ”Cclass” something of a tautology, though it is an accurate reflection of the fact that it is morethan the simple ”Type” described by a T class.C CLASSES5is common when using the cleanup stack, since the function invokedwhen pushing a CBase-derived object onto the cleanup stack is theCCleanupStack::PushL(CBase* aPtr) overload.If CCleanupStack::PopAndDestroy() is called for that object (orif a leave occurs), the object is deleted through the CBase pointer.
Thevirtual destructor in CBase ensures that C++ calls the destructors of thederived class(es) in the correct order (starting from the most derived classand calling up the inheritance hierarchy). So, as you’ll have gathered,unlike T classes, it’s fine for a C class to have a destructor, and theyusually do.It is worth noting at this point that, if the class does not derivefrom CBase and is pushed onto the cleanup stack, the CCleanupStack::PushL(TAny* aPtr) overload will be used instead. As described above, when PopAndDestroy() is called, or a leave occurs, thememory for the object will be deallocated but no destructor calls made.So if you do not inherit from CBase, directly or indirectly, even if yourbase class destructor has a virtual destructor, objects of your class willnot be cleaned up as you expect.The second characteristic of CBase, and its derived classes, is thatit overloads operator new to zero-initialize an object when it is firstallocated on the heap.
This means that all member data in a CBasederived object will be zero-filled when it is first created. You don’t haveto do this explicitly yourself in the constructor. Zero initialization willnot occur for stack objects because allocation on the stack does not useoperator new. This could potentially cause unexpected or differentbehavior between zero-filled heap-based and non-zeroed stack-basedobjects.
For this reason, among others such as managing cleanup inthe event of a leave, objects of a C class must always be allocated onthe heap.Clearly, when it is no longer needed, a heap-based object must bedestroyed. Objects of C classes typically exist either as pointer membersof another class or are accessed by local pointer variables. If owned,the CBase-derived object should be destroyed by a call to delete, forexample in the destructor of the owning class.
If the C class object isnot owned, instead being accessed through a local pointer, that pointermust be placed on the cleanup stack prior to calling any code with thepotential to leave – otherwise it will be orphaned on the heap in the eventof a leave. I’ll discuss this further in Chapter 3.If you look at e32base.h, you’ll notice that CBase declares a privatecopy constructor and assignment operator. This is a common strategyused to prevent a client from making accidental shallow copies of, orassignments to, objects of a class.
If such an operation is valid for yourclass you must explicitly declare and define a public copy constructorand assignment operator, because their private declaration in the baseclass means they cannot be called implicitly. However, given the nature6CLASS NAME CONVENTIONS ON SYMBIAN OSof C classes, a deep copy operation may well have the potential toleave, and you should never allow a constructor (or destructor) to leave(I describe the reasons for this in more detail in Chapter 4). If you needto allow C class copying, rather than defining and implementing a publiccopy constructor, you should add a leaving function, e.g.
CloneL() orCopyL(), to your class to perform the same role.Since most C classes tend not to be straightforward enough to be bitwisecopied, the implicit copy is best avoided, and this is yet another benefitof deriving from CBase. The private declaration of a copy constructorand assignment operator in the CBase class means you don’t have toprivately declare them in every C class you write in order to preventclients from making potentially unsafe ”shallow” copies.Objects of a C class must always be allocated on the heap.1.4 R ClassesThe ”R” which prefixes an R class indicates a resource, usually a handleto an external resource such as a session with the file server.