Symbian OS Explained - Effective C++ Programming For Smartphones (2005) (779885), страница 10
Текст из файла (страница 10)
After creating the object, a function which may potentiallyleave, InitializeL(), is called. The heap-based object is not leavesafe and neither the heap memory it occupies, nor any objects it owns,are destroyed if InitializeL() leaves.1void UnsafeFunctionL(){CClanger* clanger = new (ELeave) CClanger();clanger->InitializeL(); // Potential leaving function orphans clanger...
// Omitted for claritydelete clanger;}You could remove the potential memory leak by placing a TRAP, orTRAPD, macro around the call to InitializeL() to catch any leaves.However, as Chapter 2 explained, the use of TRAPs should be limited,where possible, to optimize the size and run-time speed of the compiledbinary. I have only shown one call which could cause the object to beleaked; in a typical function there may be other operations that mayleave. It would be inefficient to surround each with a TRAP. From anerror-handling perspective, too, it’s often preferable to leave rather thanreturn an error, as I discussed in Chapter 2.When you combine both arguments, and consider the following irredeemably inefficient code, it’s clear that there should be an alternative tothe TRAP idiom to protect objects which are not inherently leave-safe:void SafeButInefficientL(){CClanger* clanger = new (ELeave) CClanger();TRAPD(r,clanger->InitializeL()); // leave-safe, at a cost...if (KErrNone!=r)1Before going any further, I should point out that the example is atypical of how a CBasederived class should be instantiated.
The Symbian OS coding standard recommends that allC classes wrap their construction and initialization code in a single static function, generallycalled NewL() or NewLC(), as illustrated later. To prevent a client from instantiatingand initializing the object separately, as I’ve shown above, the standard advises that theconstructors and initialization functions should be specified as protected in the class. Thisis known as two-phase construction, and is described further in Chapter 4.USING THE CLEANUP STACK31{delete clanger; // delete clanger to prevent a leakUser::Leave(r); // leave with the same error...
horrible!}// Now do something else that may leaveTRAP(r, clanger->DoSomethingElseL());if (KErrNone!=r){delete clanger; // Again, delete clanger to prevent a leakUser::Leave(r);}... // Omitted for claritydelete clanger;}When a leave occurs, local variables are destroyed without first freeing any resources they own. The resource becomes unrecoverable,causing a memory leak, and is said to be ”orphaned”.3.1 Using the Cleanup StackThis is the cue for the cleanup stack, which is accessed through the staticmember functions of class CleanupStack, defined in e32base.h:class CleanupStack{public:IMPORT_C static void PushL(TAny* aPtr);IMPORT_C static void PushL(CBase* aPtr);IMPORT_C static void PushL(TCleanupItem anItem);IMPORT_C static void Pop();IMPORT_C static void Pop(TInt aCount);IMPORT_C static void PopAndDestroy();IMPORT_C static void PopAndDestroy(TInt aCount);IMPORT_C static void Check(TAny* aExpectedItem);inline static void Pop(TAny* aExpectedItem);inline static void Pop(TInt aCount, TAny* aLastExpectedItem);inline static void PopAndDestroy(TAny* aExpectedItem);inline static void PopAndDestroy(TInt aCount,TAny* aLastExpectedItem);};Objects that are not otherwise leave-safe should be placed on thecleanup stack before calling code that may leave.
This ensures they aredestroyed correctly if a leave occurs; in the event of a leave, the cleanup32THE CLEANUP STACKstack manages the deallocation of all objects which have been placedupon it.The following code illustrates a leave-safe but simpler version than theSafeButInefficientL() example:void SafeFunctionL(){CClanger* clanger = new (ELeave) CClanger;// Push onto the cleanup stack before calling a leaving functionCleanupStack::PushL(clanger);clanger->InitializeL();clanger->DoSomethingElseL()// Pop from cleanup stackCleanupStack::Pop(clanger);delete clanger;}If InitializeL() succeeds, the lines of code following the call popthe automatic CClanger pointer from the cleanup stack and delete theobject to which it points.
This code could equally well be replaced bya single call to CleanupStack::PopAndDestroy(clanger) whichperforms both the pop and a call to the destructor in one step. Thefunction is useful if the object is no longer required and may also bedestroyed. And if InitializeL() or DoSomethingElseL() leave,the clanger object is destroyed by the cleanup stack itself as part ofleave processing, which I’ll discuss shortly.Let’s move on to consider some general good practices when working with the cleanup stack.
Objects are pushed on and popped offthe cleanup stack in strict order: a series of Pop() calls must occurin the reverse order of the PushL() calls. It is possible to Pop() orPopAndDestroy() one or more objects without naming them, but it’sa good idea to name the object popping off. This averts any potential cleanup stack ”imbalance” bugs which can occur when Pop()removes an object from the cleanup stack which was not the oneintended.void ContrivedExampleL(){// Note that each object is pushed onto the cleanup stack// immediately it is allocated, in case the succeeding allocation// leaves.CSiamese* sealPoint = NewL(ESeal);CleanupStack::PushL(sealPoint);CSiamese* chocolatePoint = NewL(EChocolate);CleanupStack::PushL(chocolatePoint);CSiamese* violetPoint = NewL(EViolet);CleanupStack::PushL(violetPoint);CSiamese* bluePoint = NewL(EBlue);CleanupStack::PushL(bluePoint);USING THE CLEANUP STACK33sealPoint->CatchMouseL();// Other leaving function calls, some of which use the cleanup stack...//////////////////Various ways to remove the objects from the stack and delete them:(1) All with one anonymous call - OK, but potentially riskyCleanupStack::PopAndDestroy(4);(2) All, naming the last object - BetterCleanupStack::PopAndDestroy(4, sealPoint);(3) Each object individually to verify the code logicNote the reverse order of Pop() to PushL()This is quite long-winded and probably unnecessary in thisexampleCleanupStack::PopAndDestroy(bluePoint);CleanupStack::PopAndDestroy(violetPoint);CleanupStack::PopAndDestroy(chocolatePoint);CleanupStack::PopAndDestroy(sealPoint);}At times, it’s quite difficult to keep track of what’s on the cleanup stack.Imbalance bugs can be difficult to find because they cause unexpectedpanics in code quite unrelated to where the error has occurred.
To avoidthem, it’s best to name what you Pop() off the cleanup stack explicitly,if only in the early stages of coding until you’re confident that the codeis behaving as you expect. The checking is only performed in debugbuilds, so it has no impact on the speed of released code (although, ifyou do want to perform checking in a release build, you can use theCleanupStack::Check() function). If nothing else, it may help youtrack down bugs such as the one below where, under some conditions, anobject is accidentally left on the cleanup stack when the function returns:void AnotherContrivedExampleL(const TDesC& aString){CClanger* clanger = AllocateClangerL(aString);CleanupStack::PushL(clanger);TBool result = NonLeavingFunction(clanger);if (!result)return;// Whoops, clanger is still on the cleanup stack!DoSomethingThatLeavesL();CleanupStack::PopAndDestroy(clanger);}But when should you Pop() an object from the cleanup stack? Well, itshould never be possible for an object to be cleaned up more than once.If a pointer to an object on the cleanup stack is later stored elsewhere,say as a member variable of another object which is accessible after aleave, it should then be popped from the cleanup stack.
If the pointerwere retained on the cleanup stack, it would be destroyed by it, but theobject storing the pointer to it would also attempt to destroy it, usually in34THE CLEANUP STACKits own destructor. Objects should be referred to either by another objector by the cleanup stack, but not by both.void TransferOwnershipExampleL{CClanger* clanger = new (ELeave) CClanger();CleanupStack::PushL(clanger); // Pushed - the next function may leaveiMemberObject->TakeOwnershipL(clanger);// iMemberObject owns it soCleanupStack::Pop(clanger);// remove from cleanup stack}Likewise, you should never push class member variables (objectsprefixed by ”i”, if the Symbian OS naming convention is followed) ontothe cleanup stack.
The object may be accessed through the owning objectwhich destroys it when appropriate, typically in its destructor. Of course,this requires the owning object to be leave-safe.Incidentally, no panic occurs if you push or pop objects onto thecleanup stack more than once.
The problem occurs if the cleanup stacktries to destroy the same object more than once, either through multiplecalls to PopAndDestroy() or in the event of a leave. Multiple deletionof the same C class object will attempt to free memory which has alreadybeen released back to the heap, which causes a panic.If an object is pushed onto the cleanup stack and remains on it whenthe function returns, the function name must have the suffix ”C”.