Symbian OS Explained - Effective C++ Programming For Smartphones (2005) (779885), страница 11
Текст из файла (страница 11)
Thisindicates to the caller that, when the function returns successfully, thecleanup stack has additional objects upon it. It is typically used by CBasederived classes which define static functions to instantiate an instance ofthe class and leave it on the cleanup stack. The C suffix indicates to thecaller of a function that it is not necessary to push any objects allocatedby the function onto the cleanup stack. The following code creates anobject of type CSiamese (which I used in an earlier example) and leavesit on the cleanup stack.
This function is useful because the caller caninstantiate CSiamese and immediately call a leaving function, withoutneeding to push the allocated object back onto the cleanup stack:CSiamese* CSiamese::NewLC(TPointColor aPointColour){CSiamese* me = new (ELeave) CSiamese(aPointColour);CleanupStack::PushL(me); // Make this leave-safe...me->ConstructL();return (me) // Leave "me" on the cleanup stack for caller to Pop()}However, a word of warning with regard to functions that leave objectson the cleanup stack – you must take care when calling them from insidea TRAP harness.
If objects are pushed onto the cleanup stack inside aTRAP and a leave does not occur, they must be popped off again beforeHOW DOES THE CLEANUP STACK WORK?35exiting the macro, otherwise a panic occurs. This is because the cleanupstack stores nested levels of objects to destroy; each level is confinedwithin a TRAP, and must be empty when the code inside it returns.
Thus,the following code panics with E32USER-CBASE 71,2 when it returns tothe TRAPD macro.CExample* MakeExample(){CExample* pExample = TRAPD(r, CExample::NewLC()); // This will panicreturn (pExample);}Objects that are not leave-safe should be placed on the cleanupstack before calling code that may leave. The cleanup stack managesthe deallocation of all objects which have been placed upon it inthe event of a leave.3.2 How Does the Cleanup Stack Work?I’ll use this section to go into how the cleanup stack works in somedetail. If you came to this chapter only with an interest in how to usethe cleanup stack, feel free to skip this section, since I have now coveredthe most important points for CBase-derived classes. Either read onanyway, or rejoin the chapter at Section 3.3, ”Using the Cleanup Stackwith non-CBase Classes”, where I’ll go into how to use the cleanup stackto make leave-safe objects of R classes, heap-based T classes and objectsreferenced through M class pointers.As I’ve already described, the cleanup stack stores pointers to objectsto be destroyed in the event of leave.
The pointers are stored in nested”levels” associated with the TRAP macro under which they were pushedonto the stack. In the following code, if FeedL() leaves, only objectsplaced on the cleanup stack inside the TRAP macro are destroyed. Thusthe CChilli object on the cleanup stack is destroyed, but the CExampleobject, stored on the cleanup stack prior to the call inside the TRAP macro,is not destroyed because the leave is only propagated to the level of theenclosing TRAP.class CChilli;class TCalibrateDragon2The panic is described as follows in the panic documentation of the SDK: ”This panicis raised when TRAPs have been nested and an attempt is made to exit from a TRAP nestlevel before all the cleanup items belonging to that level have been popped off the cleanupstack.”36THE CLEANUP STACK{public:FeedL(TChilliPepper aChilli);private:void TasteChilliL(CChilli* aChilli);... // Omitted for clarity};CExample* CExample::NewL(TChilliPepper aChilli){CExample* me = new (ELeave) CExample();CleanupStack::PushL(me); // Later functions may leaveTCalibrateDragon dragon;TRAPD(r, dragon->FeedL(aChilli)); // TRAP this to act on the result// Do something dependent on whether FeedL() leftif (KErrNone==r){//...
omitted for clarity}else{//... omitted for clarity}CleanupStack::Pop(me);return (me)}void TCalibrateDragon::FeedL(TChilliPepper aChilli){CChilli* chilli = CChilli::NewL(aChilli);CleanupStack::PushL(chilli);TasteChilliL(chilli); // Function leaves if chilli is too hotCleanupStack::PopAndDestroy(chilli);}As the example shows, in the event of a leave, the cleanup stackdestroys the objects it stores for that particular TRAP level. But how doesit know how to destroy them? What does the cleanup stack class looklike? How is the cleanup stack created? And what, the ever-cautiousreader may ask, happens if CleanupStack::PushL() leaves?Well, let’s start at the beginning, the creation of a cleanup stack. Youwon’t have to create a cleanup stack if you are writing a GUI applicationor a server, since both have cleanup stacks created for them as partof their framework code. However, if you are writing a simple consoletest application or creating a separate thread, and you need to use thecleanup stack or call code that does, you will need to allocate a cleanupstack, since if there is no cleanup stack allocated for a thread, a call toCleanupStack::PushL() causes an E32USER-CBASE 32 panic.CTrapCleanup* theCleanupStack = CTrapCleanup::New();...
// Code that uses the cleanup stack, within a TRAP macrodelete theCleanupStack;HOW DOES THE CLEANUP STACK WORK?37The name given to the CTrapCleanup object is up to you, since theobject is only referenced directly when you come to destroy it again.As you’ve already seen, access to the cleanup stack is through the staticmember functions of the CleanupStack class, defined in e32base.h.But how does this work?Well, it’s slightly confusing. When you create an object of typeCTrapCleanup, the following occurs in CTrapCleanup::New():1. The current trap handler for the thread is stored.2. Within CTrapCleanup, an iHandler object of type TCleanupTrapHandler is created (this owns a CCleanup object whichactually contains the code which implements the cleanup stack).3.
A call to User::SetTrapHandler() then installs that TCleanupTrapHandler object as the new trap handler for the thread.When you call CleanupStack::PushL() or CleanupStack::Pop(), the static functions call User::TrapHandler() to acquirethe installed TCleanupTrapHandler object and gain access to theCCleanup object. Likewise, when a leave occurs, the trap handlercatches it and invokes the CCleanup object to cleanup any objects ithas stored (see Figure 3.1).The CleanupStack class has three overloads of the PushL() function, which I’ll discuss shortly.
But first, let me allay any concerns youmay have about the fact that PushL() can leave.The reason that PushL() is a leaving function is because it mayallocate memory to store the cleanup pointer and, thus, may fail in lowmemory situations. However, you don’t need to worry that the objectyou are pushing onto the cleanup stack will be orphaned if a leave doesoccur.
The cleanup stack is created with at least one spare slot. Whenyou call PushL(), the pointer you pass is added to the vacant slot, andthen, if there are no more vacant slots available, the cleanup stack codeattempts to allocate one for the next PushL() operation. If that allocationfails, a leave occurs, but your pointer has already been stored safely andthe object it refers to is safely cleaned up.In fact, the cleanup stack code is more efficient than my simplisticexplanation above; it allocates more than a single slot at a time.
Bydefault, it allocates four slots. In addition, it doesn’t release slots whenyou Pop() objects out of them, so a PushL() frequently does not makeany allocation and can be guaranteed to succeed. This can be usefulin circumstances where you acquire more than one pointer that is notleave-safe to push onto the cleanup stack. You can use this knowledgeto expand the cleanup stack to contain at least the number of slots youneed, then create the objects and safely push them onto the vacant slots.38THE CLEANUP STACKCTrapCleanupCleanupStackCTrapCleanup* New()...static void PushL(CBase*)static void PushL(TAny*)static void PushL(TCleanupItem)static void Pop()static void PopAndDestroy()...iHandlerTCleanupTrapHandleriOldHandlerTTrapHandlerTrap()UnTrap()...iCleanupCCleanupvoid PushL(CBase*)void PushL(TAny*)void PushL(TCleanupItem)void Pop()void PopAndDestroy()...Figure 3.1 Cleanup stack class hierarchyThe cleanup stack stores pointers to objects to be destroyed usingnested ”levels” associated with the TRAP macro under which theywere PushL()’d.3.3 Using the Cleanup Stack with Non-CBase ClassesIf you’ve rejoined after skipping the detailed section on how the cleanupstack works, welcome back.
Up to this point, we’ve only really considered one of the overloads of the CleanupStack::PushL() function:PushL(CBase* aPtr), which takes a pointer to a CBase-derivedobject. When CleanupStack::PopAndDestroy() is called for thatobject, or if leave processing occurs, the object is destroyed by invokingdelete on the pointer, which calls the virtual destructor of the CBasederived object. As mandated by C++, the object is destroyed by callingdestructor code in the most-derived class first, moving up the inheritanceUSING THE CLEANUP STACK WITH NON-CBase CLASSES39hierarchy calling destructors in turn and eventually calling the emptyCBase destructor.
This is the reason that the CBase class has a virtualdestructor – so that C class objects can be placed on the cleanup stackand destroyed safely if a leave occurs.If you define a C class and forget to derive it from CBase, badthings happen if the cleanup stack tries to destroy it. If the objectdoes not derive from CBase, the CleanupStack::PushL(TAny*)overload will be used to push it onto the cleanup stack, instead ofthe CleanupStack::PushL(CBase*) overload. Using this overloadmeans that if the cleanup stack later comes to destroy the object, itsheap memory is simply deallocated (by invoking User::Free()) andno destructors are called on it.