Symbian OS Explained - Effective C++ Programming For Smartphones (2005) (779885), страница 5
Текст из файла (страница 5)
There is noequivalent RBase class, so a typical R class will have a constructor toset its resource handle to zero, indicating that no resource is currentlyassociated with the newly constructed object. You should not attempt toinitialize the resource handle in the constructor, since it may fail; youshould never leave in a constructor, as I’ll explain in Chapter 4.To associate the R class object with a resource, the R class will typically have a function such as Open(), Create() or Initialize()which will allocate the resource and set the handle member variable asappropriate or fail, returning an error code or leaving. The class will alsohave a corresponding Close() or Reset() method, which releases theresource and resets the handle value to indicate that no resource is associated with the object. It should thus be safe to call such a function more thanonce on the same object.
Although, in theory, the cleanup function canbe named anything, by convention it is almost always called Close().A common mistake when using R classes is to forget to call Close() orto assume that there is a destructor which cleans up the owned resource.This can lead to serious memory leaks.R classes are often small, and usually contain no other memberdata besides the resource handle. It is rare for an R class to have adestructor – it generally does not need one because cleanup is performedin the Close() method.R classes may exist as class members or as automatic variables onthe stack, or occasionally on the heap. You must ensure the resourceM CLASSES7will be released in the event of a leave, typically by using the cleanupstack, as described in Chapter 3.
Remember, if using an R class object asa heap-based automatic variable, you must make sure that the resourceis released as well as freeing the memory associated with the objectitself, typically by using two push calls: CleanupClosePushL(), or asimilar function, to ensure that the resource is cleaned up, and a standardCleanupStack::PushL(TAny*) which simply calls User::Free()on the heap cell.The member data of an R class is typically straightforward enoughto be bitwise copied, so you would not expect to see an explicit copyconstructor or assignment operator unless a shallow copy would resultin undefined behavior.
An example of this might be where the duplication of a handle value by bitwise copy would make it ambiguous asto which object owns the resource. In these circumstances undefinedbehavior might result if both copies attempt to release it. Calling theClose() function on the same object may be safe because the handlevalue member is reset, but the same cannot always be said for callingClose() twice on the same underlying resource via two separate handleobjects. If one object frees the resource, the handle held by the otheris invalid.If your class contains a handle to a resource which cannot be sharedsafely by bitwise copy, you should declare a cloning function in the eventthat it must be copied. If you want to prevent any kind of copying forobjects of your R class, you should declare, but not define, a private copyconstructor and assignment operator.The rules for R classes are more open to interpretation than C or Tclasses, so you’ll find more different ”species” of R class.
Within SymbianOS the types of resources owned by R classes vary from ownership of afile server session (class RFs) to ownership of memory allocated on theheap (class RArray).Close() must be called on an R class object to cleanup its resource.1.5 M ClassesComputing folklore relates that ”mix-ins” originated from Symbolic’sFlavors, an early object-oriented programming system.
The designerswere apparently inspired by Steve’s Ice Cream Parlor, a favorite icecream shop of MIT students, where customers selected a flavor of icecream (vanilla, strawberry, chocolate, etc) and added any combinationof mix-ins (nuts, fudge, chocolate chips and so on).8CLASS NAME CONVENTIONS ON SYMBIAN OSWhen referring to multiple inheritance, it implies inheriting from amain ”flavor” base class with a combination of additional ”mix-in”classes which extend its functionality. I believe the designers of SymbianOS adopted the term ”mixin”, and hence the M prefix in turn, althoughthe use of multiple inheritance and mixins on Symbian OS should bemore controlled than a trip to an ice cream parlor.In general terms, an M class is an abstract interface class.
A concreteclass deriving from such a class typically inherits a ”flavor” base class(such as a CBase, or a CBase-derived class) as its first base class, and oneor more M class ”mixin” interfaces, implementing the interface functions.On Symbian OS, M classes are often used to define callback interfacesor observer classes.An M class may be inherited by other M classes. I’ve shown twoexamples below. The first illustrates a concrete class which derives fromCBase and a single mixin, MDomesticAnimal, implementing the functions of that interface. The MDomesticAnimal class itself derives from,but does not implement, MAnimal; it is, in effect, a specialization of thatinterface.class MAnimal{public:virtual void EatL() =0;};class MDomesticAnimal : public MAnimal{public:virtual void NameL() =0;};class CCat : public CBase, public MDomesticAnimal{public:virtual void EatL(); // From MAnimal, via MDomesticAnimalvirtual void NameL(); // Inherited from MDomesticAnimal...// Other functions omitted for clarity};The second example illustrates a class which inherits from CBase andtwo mixin interfaces, MRadio and MClock.
In this case, MClock is nota specialization of MRadio and the concrete class instead inherits fromthem separately and implements both interfaces. For M class inheritance,various combinations of this sort are possible.class MRadio{public:virtual void TuneL() =0;};M CLASSES9class MClock{public:virtual void CurrentTimeL(TTime& aTime) =0;};class CClockRadio : public CBase, public MRadio, public MClock{public:virtual void TuneL();virtual void CurrentTimeL(TTime& aTime);...
// Other functions omitted for clarity};The use of multiple interface inheritance, as shown in the previousexamples, is the only form of multiple inheritance encouraged on SymbianOS. Other forms of multiple inheritance can introduce significant levelsof complexity, and the standard base classes were not designed with it inmind. For example, multiple inheritance from two CBase-derived classeswill cause problems of ambiguity at compile time, which can only besolved by using virtual inheritance:class CClass1 : public CBase{...};class CClass2 : public CBase{...};class CDerived : public CClass1, public CClass2{...};void TestMultipleInheritance(){// Does not compile, CDerived::new is ambiguous// Should it call CBase::new from CClass1 or CClass2?CDerived* derived = new (ELeave) CDerived();}Let’s consider the characteristics of M classes, which can be thoughtof as equivalent to Java interfaces.
Like Java interface, an M classshould have no member data; since an object of an M class is neverinstantiated and has no member data, there is no need for an M class tohave a constructor.In general, you should also consider carefully whether an M classshould have a destructor (virtual or otherwise). A destructor places arestriction on how the mixin is mixed in, forcing it to be implementedonly by a CBase-derived class.
This is because a destructor means thatdelete will be called, which in turn demands that the object cannotreside on the stack, and must always be heap-based. This implies thatan implementing class must derive from CBase, since T classes neverpossess destructors and R classes do so only rarely.10CLASS NAME CONVENTIONS ON SYMBIAN OSIn cases where the ownership of an object that implements an M classis through a pointer to that interface class, it is necessary to provide somemeans of destroying the object. If you know you can restrict implementersof your interface class to derivation from CBase, then you can providea virtual destructor in the M class.
This allows the owner of the M classpointer to call delete on it.If you do not define a virtual destructor, deletion through an M classpointer results in a USER 42 panic, the reasons for which are as follows:a concrete class that derives from an M class also inherits from anotherclass, for example CBase, or a derived class thereof.
The mixin pointershould be the second class in the inheritance declaration order3 , and theM class pointer will thus have been cast to point at the M class subobjectwhich is some way into the allocated heap cell for the object, rather thanat the start of a valid heap cell.The memory management code attempting to deallocate the heapspace, User::Free(), will panic if it cannot locate the appropriatestructure it needs to cleanup the cell. This is resolved by using a virtualdestructor.However, as an alternative to defining the interface as an M class,you should consider simply defining it as an abstract CBase-derivedclass instead, which can be inherited as usual by a C class.