Wiley.Developing.Software.for.Symbian.OS.2nd.Edition.Dec.2007 (779887), страница 23
Текст из файла (страница 23)
As with all stacks, items are popped from the cleanup stack ona last-in/first-out basis and the stack must be kept balanced in order toperform as expected.Symbian OS provides a static API class called CleanupStackfor accessing the cleanup stack. The basic functions in this classare: CleanupStack::PushL() and CleanupStack::Pop().
Thesefunctions push items to and pop items from the cleanup stack, respectively. The following example shows the cleanup stack in use.Func1L(){CMyObject *myObj = new (ELeave) CMyObject;CleanupStack::PushL(myObj);CMyOtherObject *myOtherObj = new (ELeave) CMyOtherObject;CleanupStack::PushL(myOtherObj)DoSomethingL();CleanupStack::Pop(2, myObj); // Pop last two items off cleanup stackdelete myOtherObj;delete myObj;}Both myObj and myOtherObj are pushed onto the cleanup stackwith CleanupStack::PushL(). If function DoSomethingL() leaves,execution stops at that point; however, before control is returned tothe calling function, each pointer on the cleanup stack (myObj andmyOtherObj in this case) is freed.If DoSomethingL() does not leave (normal case), the items are poppedoff the cleanup stack manually via CleanupStack::Pop(2, myObj).EXCEPTION ERROR HANDLING AND CLEANUP107The argument ‘2’ means to remove the last two items pushed, and passingmyObj is optional, but allows the cleanup stack to check in debug buildsthat the last object you think you are popping off is the correct one, thushelping you eliminate programming errors.Note that when each object is created in the example, there is an(ELeave) after new.
This causes a leave if the memory allocation fails.See section 4.5.11 for more details on how this works.A variation of the pop function is CleanupStack::PopAndDestroy(). This function removes the item from the cleanup stackand deallocates it. The item is cleaned up in the same way it would be ifa leave had occurred.
Since popping and deallocating are often done atone time, this function is convenient. In the previous example, CleanupStack::PopAndDestroy(2, myObj) could be used to replaceCleanupStack::Pop(2, myObj) and the two delete statements.Note that you should not push class member variables onto thecleanup stack. Any pointers allocated and assigned as member variablesshould be deleted in your object’s destructor. If you push them onto thecleanup stack, you risk the possibility that deletion will be attemptedtwice, resulting in a crash.
Only pointers allocated within a function asautomatic variables should be pushed to the cleanup stack since oncethe function exits, there is no chance to free those pointers.Before the cleanup stack can be used, it must be created for the threadthat uses it. You will not have to worry about this for GUI applicationsand servers, since the cleanup stack is created automatically in thesecases.
However, in other types of programs (or in user-created threadswithin a GUI program, for example), you will have to do this yourselfby adding CTrapCleanup *trap = CTrapCleanup::New() to yourcode and calling delete trap when finished.4.5.7 Object Types and the Cleanup StackThe following are two of the most commonly used CleanupStack::PushL() methods:•PushL(CBase *);•PushL(TAny *).If a CBase-derived object is pushed on the stack, upon cleanup (performed as a result of a leave or a CleanupStack::PopAndDestroy()call), a delete will be performed on that object, causing the object’sdestructor to be called.
Since the CBase destructor is virtual, the destructor of the derived class is called. This is the ideal cleanup case.If a non-CBase object is pushed on the cleanup stack (causing thePushL(TAny *) version of the function to be called), then the corresponding PopAndDestroy() function does not call delete on the108SYMBIAN OS PROGRAMMING BASICSpushed pointer, but instead calls User::Free().
This simply frees thememory allocated to the object.The reason PopAndDestroy() does not call delete for non-CBaseobjects is that it does not know your class type and thus cannot get toyour destructor. CBase, on the other hand, has the destructor declaredas virtual so PopAndDestroy() can call delete on it knowing that ifyou have an explicit destructor defined in your CBase-derived class, itwould be called.Calling PopAndDestroy () on non-CBase objects is fine for objectswithout explicit destructors. If your object does have an explicit destructor,however, make sure it is derived from CBase so that PopAndDestroy()will call your destructor, otherwise your object will only be partiallydestroyed.Note that PushL() can itself leave if there is insufficient memory.However, it will only leave after the item is pushed on the cleanup stack,so you can be sure that the item will be cleaned up even when PushL()fails.4.5.8 More Complex CleanupIn some cases, deleting memory that is referenced by automatic pointersis not the only type of cleanup that is needed if a leave occurs.
You mayhave application-specific cleanup (e.g., tidying up a state machine in afile), or may need to call specific methods in automatic objects beforethey go out of scope (e.g., Close()).To handle the just-mentioned requirement, Symbian OS providesanother CleanupStack::PushL() overloaded function:PushL(TCleanupItem aUserCleanup)Using this form of PushL(), you push a reference to your own cleanuphandling function on to the cleanup stack. Upon cleanup (via a leaveor PopAndDestroy() call), your function is invoked when this item isretrieved from the cleanup stack. TCleanupItem is a wrapper class fora simple function call that returns void and takes one TAny* argument.A code example should clarify this:void MyCleanupFunc(TAny *aParam){// Will execute on leave or PopAndDestroy. Do special cleanup here.}void Foo(){TInt data=1;CleanupStack::PushL(TCleanupItem(MyCleanupFunc,&data));// ...Func1L();EXCEPTION ERROR HANDLING AND CLEANUP109// ...CleanupStack::PopAndDestroy();}If Func1L() leaves, MyCleanupFunc() will execute with the argument set to &data (in this case data is an integer variable containing 1for an example argument).
Note that in the normal case where Func1L()does not leave, MyCleanupFunc() will execute when CleanupStack::PopAndDestroy() is called.4.5.9 Other Cleanup FunctionsThere are three more cleanup stack functions that are useful: CleanupClosePushL(), CleanupReleasePushL(), and CleanupDeletePushL(). These are static API functions that do not belong to anyclass. These functions use a combination of C++ templates and theTCleanupItem form of CleanupStack::PushL(), just described, toimplement their functionality.CleanupClosePushL<class T>(T& obj)This function will push obj on the cleanup stack. When cleanup occurs(via leave or PopAndDestroy()), obj.Close() is called. This isperfect for resource classes (‘R’) that are allocated on the stack andrequire the Close() method to be called to clean up. The followingexample shows this function in action:void FooL(){RFile f;...f.Open(...);...CleanupClosePushL(f);Func1L(); // may leave, if so f.Close() called...CleanupStack::PopAndDestroy(); // f.Close() called}You do not need to add the template declaration after CleanupClosePushL() (or any of the three functions of this section), since thecompiler can unambiguously determine the class type for the templatefrom the function argument.CleanupReleasePushL <class T>(T& obj)CleanupReleasePushL() acts the same as CleanupClosePushL()except that method Release() is called on cleanup.
Calling Release()is required to clean up some interfaces.110SYMBIAN OS PROGRAMMING BASICSCleanupDeletePushL <class T>(T *obj)Pushing an object on the cleanup stack using this function will causea delete to be called on obj upon cleanup. How is this differentfrom CleanupStack::PushL()? Since CleanupDeletePushL()uses templates, the class type of the object is passed in addition tothe object itself. This enables the actual destructor of the passed objectto be called upon cleanup regardless of the object’s type. Contrast thiswith CleanupStack::PushL(CBase*), where the passed class mustbe derived from CBase.You may ask why CleanupDeletePushL() is ever needed sinceyou can derive any classes you have destructors for from CBase anduse CleanupStack::PushL(CBase*).
This is a good question, andin fact Symbian recommends you do just that. However, a good usefor CleanupDeletePushL() is when you are referencing CBasederived objects through M class pointers. CleanupStack::PushL()will not recognize an object referenced by an M class pointer as beingderived from CBase even though the object typically is through multipleinheritance. Calling CleanupDeletePushL() in this case will causethe object’s destructor to be called.4.5.10 LC FunctionsFunctions that end in LC provide an added convenience – upon successfulcompletion the function returns with the object allocated and on thecleanup stack. This can be illustrated as follows:void Func1L(){TInt *buffPtr = User::AllocLC(1000); // system static API which//allocates memory...FooL();CleanupStack::PopAndDestroy(buffPtr);}In Func1(), if User::AllocLC() allocates memory successfully,it returns the buffer pointer to BuffPtr and pushes that pointer on thecleanup stack.
This saves you a statement, but don’t forget to pop thepointers off the cleanup stack after calling LC functions!4.5.11 Leaves when Creating ObjectsWhen an object is constructed using the new operator, a memory allocation occurs. Although a return value of NULL will indicate that thememory allocation failed, many times you will want it to generate a leaveEXCEPTION ERROR HANDLING AND CLEANUP111instead. How can you do this? Just insert an (ELeave) between the newand the class name as in the example below:CMyObject *obj = new(ELeave) CMyObject;This may seem cryptic at first, but it’s valid C++ syntax for invoking anoverloaded new operator function.For a traditional new statement (e.g., CMyObject obj = new CMyObject), the compiler invokes the built-in new function prototyped asnew(TInt)– the TInt argument being the size of the object.
However, if you add (ELeave) after the new keyword, the compiler invokesthe function prototyped as new(TInt, TLeave) instead, where TIntis the object’s size and TLeave is the data type for the argumentELeave. ELeave is just a dummy variable whose purpose is to causean overloaded new function to be invoked. Symbian OS implements thisoverloaded new function (overriding C++’s built-in new function) to leaveon memory allocation failures.Remember to take care in cleanup when constructing objects usingELeave, since there is the possibility that a leave can occur during construction. For example, can you spot the error in the following code?Func1L(){CMyClass *obj1 = new(ELeave) CMyClass;CMyClass *obj2 = new(ELeave) CMyClass;CleanupStack::PushL(obj1);CleanupStack::PushL(obj2);Call1L();Call2L();CleanupStack::PopAndDestroy(2, obj1);}The problem is that if a leave occurs when constructing the obj2CMyClass object, then obj1 will not be destroyed.