Wiley.Developing.Software.for.Symbian.OS.2nd.Edition.Dec.2007 (779887), страница 24
Текст из файла (страница 24)
You must push obj1before creating the second CMyClass instance (obj2). The correctedcode is as follows:Func1L(){CMyClass *obj1 = new(ELeave) CMyClass;CleanupStack::PushL(obj1);CMyClass *obj2 = new(ELeave) CMyClass;CleanupStack::PushL(obj2);Call1L();Call2L();CleanupStack::PopAndDestroy(2, obj11);}112SYMBIAN OS PROGRAMMING BASICSThe original code has an additional problem, in that if the PushL()of obj1 leaves, then obj2 will not be deleted. However, obj1 will becleaned up correctly in the event that PushL(obj1) leaves, becausethe cleanup stack guarantees not to leave until the pointer pushed to it issafely stored.4.5.12 Leaves in ConstructorsWe have seen how adding ELeave will cause a new to leave if a memoryallocation occurs, but what if a leave occurs in the class constructor itself?This is a problem in Symbian OS, and thus is not allowed.
Why? Becausethe constructor is called immediately (and behind the scenes) after thememory allocation in the new operator function, with no chance for theprogrammer to push the pointer to the allocated memory to the cleanupstack. So if a leave occurs during the constructor, you have an orphanedpointer to the memory allocated for the class.In other words, a constructor should never leave, so don’t call leave inthem, or call any functions that may leave (i.e., with L suffix) unless youtrap them. But isn’t that unrealistic? Surely you may want to do memoryallocations – or otherwise call functions that may leave – when an objectis constructed? This is why Symbian OS commonly uses the idiom knownas two-phase construction.4.5.13 Two-Phase ConstructionThe two-phase construction concept is simple: a method is supplied inyour class named ConstructL(), which completes the object construction.
A leave can occur in this method since it is just a normalfunction.Of course, if you can write your whole constructor without the possibility of a leave occurring, then the two-phase method is not needed.It is important to know whether an object has a ConstructL()before using it. Not calling ConstructL() on an object that relies ontwo-phase construction will result in fatal consequences.Symbian OS classes are often implemented with a static NewL()that will create the object correctly by performing both a new and theConstructL() call, as in the following code.CMyObj* MyObj::NewL(){CMyObj* self = new(ELeave) CMyObj;CleanupStack::PushL(self);self->ConstructL();CleanupStack::Pop(self);return self;}EXCEPTION ERROR HANDLING AND CLEANUP113Or, if you want to provide a NewLC() function, you could implementboth, as shown below.CMyObj* MyObj::NewL(){CMyObj* self = NewLC();CleanupStack::Pop(self);return self;}CMyObj* MyObj::NewLC() // the returned object pointer will be on cleanup// stack on exit, to save user from pushing.{CMyObj* self = new(ELeave) CMyObj;CleanupStack::PushL(self);self->ConstructL();return self;}4.5.14 PanicsA panic occurs on any error that is not recoverable, at which timethe thread exits immediately and the system displays a pop-up withinformation regarding the error (the Symbian OS Library contains a list ofthe system panics).
In general, a panic occurs as a result of a programmingerror of some kind. An example is if you use an API improperly. Forexample, if you try to write to a file at a specific offset via the Write()API function of class RFile, but your offset passed to the function isnegative, a panic results.A panic consists of a category name as well as a reason number – theSDK documentation contains a list of these, with a description of whatthey mean.You can invoke a panic in your code in response to an error you detectby calling:User::Panic(const TDes& aCategory, TInt aReason);TDes will be covered in Chapter 6, but for now you only need toknow that it represents a Symbian string.
User::Panic() will causethe thread to exit and an information box to appear indicating the threadname, as well as the category name and reason code passed to thepanic function. Released code should never generate a panic condition(although, unfortunately, some does).On S60, when a panic occurs a box that simply says ‘Program Closed’is displayed or the program just closes without any message at all.To cause the full panic information to appear, you need to createa dummy file (which can be empty) named ErrRd in the directory\resource\ for S60 3rd Edition, and \system\bootdata\ for previous114SYMBIAN OS PROGRAMMING BASICSS60 versions, on the target system’s C drive. This works for both theemulator and the smartphone.
If no file manager is available on the S60smartphone, then the file can be added via a SIS file (a signed one inthe case of S60 3rd Edition). The following code shows an example ofcalling panic._LIT(KFooProgram, "Foo program");// Defines a string and assigns to// KFooProgramvoid Foo(TInt aX, TAny *aBuff){if (!aBuff){User::Panic(KFooProgram,3);}}KFooProgram is a string constant indicating the category (do not worryabout this string syntax for now), and 3 is the reason code.4.5.15Assert MacrosAssert macros, ASSERT ALWAYS and ASSERT DEBUG, are used toconfirm programming logic, as shown in the following code. The macroimplements a simple if statement, so you could replace:if (!aBuff){User::Panic(KFooProgram,3);}with one of the following lines:__ASSERT_ALWAYS(aBuff,User::Panic(KFooProgram,3));or__ASSERT_DEBUG(aBuff,User::Panic(KFooProgram,3));ASSERT DEBUG will only raise a panic in debug builds. Assertmacros should not be used to check data that could legitimately fail (forexample, you should not assert that there is an active Internet connectionbecause it is possible that a connection has dropped and an applicationshould handle this gracefully rather than simply panic and terminate).LIBRARIES1154.6 LibrariesAs Chapter 3 discussed, the two main types of library in Symbian OS arestatic interface shared libraries and polymorphic interface shared libraries(most commonly known as polymorphic DLLs).
Static interface sharedlibraries are linked at compile time. Polymorphic DLLs, on the otherhand, are loaded at runtime. In other words, the complete DLL contentsare loaded into a shared memory region and programs call the functionsin that region directly as needed. DLLs are efficient since only a singlecopy of each library function exists in memory, and these can be sharedby multiple programs.Both types of shared library can contain C++ classes.
The libraryclasses can be base classes, from which user programs derive their ownclasses. Many Symbian OS API classes fall into this category. Librariescan also contain derived, concrete classes which the user manipulates viabase class pointers (without knowledge of the details of the derived class).This capability is used by polymorphic DLLs to implement plug-ins.Of course, the library can also contain classes and functions that canbe instantiated and used directly (like RFile, RSocket, or the staticUser classes, for example).4.6.1 Creating a LibraryChapter 5 discusses the various build issues encountered with librarycode (including freezing exports and the def file inner workings). Thissection outlines a few programming points and presents the basic codestructure.These rules must be followed when writing a library:•In the H files, add IMPORT C before the declaration of each function(class method or external function) that you want to be available tolibrary users.•In the CPP files, add EXPORT C before the implementation of eachfunction you want available to library users.
There should be exactlythe same number of EXPORT C statements in the implementing CPPfile as IMPORT C statements in the corresponding header file.These statements are required in order for a function to be availablefor use by clients of the library. IMPORT C and EXPORT C are mappedto compiler-specific keywords. Some development tools may be morelenient (such as the Microsoft compiler, which lets you get away with justadding the IMPORT C’s in the header) but, for portability, you should useboth IMPORT C and EXPORT C as specified.116SYMBIAN OS PROGRAMMING BASICSThe following is an example of a skeleton shared interface library.//MyLibrary.hclass CLibraryClass{public:IMPORT_C CLibraryClass();IMPORT_C void Func1();IMPORT_C void Func2();void Func3();virtual void Func4();};//MyLibrary.cppEXPORT_C CLibraryClass::CLibraryClass(){// do stuff}EXPORT_C void CLibraryClass::CLibraryFunc1(){// do stuff}EXPORT_C void CLibraryClass::Func2(){// do stuff}void CLibraryClass::Func3(){// do stuff}void CLibraryClass::Func4(){// do stuff}In this example, the constructor, Func1(), and Func2() are exportedand available for use by other programs (indicated by IMPORT C/EXPORT C declarations).
Func3() is not exported and thus is not available – a program will get an unresolved external link error if it calls it.Func4() is available for outside use. How is that, since EXPORT Cand IMPORT C are not used? The reason is that it is virtual. All virtual functions are exported implicitly (although it does not hurt to addIMPORT C/EXPORT C).The MMP file is shown below.