Introduction to the basic use of the cleanup stack in Symbian code.
A Symbian leave is similar to a
standard C++ exception in that code execution jumps from the point
where the leave occurs to the point where the leave is trapped using
the TRAP
or TRAPD
macros. For an
introduction to the trap and leave mechanism see Symbian exception
handling.
void LeakingFunctionL() { // Allocate an object on the heap. CExample* ptr = new (ELeave) CExample; // Call a function that might leave. // Leaving functions must have an L suffix. ptr->UseL(); // Clean up, if execution reaches this point! delete ptr; }The function is not leave-safe. If the call to
CExample::UseL()
leaves the heap object pointed at by ptr
will not be deallocated. In standard C++ you might use
a smart pointer to solve this problem, ensuring that the destructor
of the smart pointer deletes the heap object in the event an exception
is thrown. Instead of this approach we can add the address of the
heap object to the cleanup stack.CleanupStack::PushL(
CBase* )
to add the address of a C object to the cleanup
stack. The cleanup stack takes ownership of the object and deletes
it in the event of a leave. For example:void NonLeakingFunctionL() { // Allocate an object on the heap. CExample* ptr = new (ELeave) CExample; // Place the address of the object on the // cleanup stack. CleanupStack::PushL( ptr ); // Call a function that might leave. // Leaving functions must have an L suffix ptr->UseL(); // Delete the heap based object. CleanupStack::PopAndDestroy(); }
CExample::UseL()
leaves the cleanup
stack automatically deletes the heap object. This occurs before execution
transfers to the trap harness. If there is no leave we call PopAndDestroy()
. This is a convenience function that is
equivalent to calling:// Remove the address from the cleanup stack. CleanupStack::Pop(); // Delete the heap object. delete ptr;Using the cleanup stack to call Close() on R objects
void FailToCloseL() { // Create an R object to use a server. RFs fileServerSession; // Open the session. User::LeaveIfError( fileServerSession.Connect() ); // Call a function that might leave. DoSomethingL(); // Clean up server resources, if execution reaches this point! fileServerSession.Close(); }This is a similar problem to the one in
LeakingFunctionL()
above. If a leave occurs we fail to close the File Server session.
The solution is to push the session onto the cleanup stack. To ensure
that Close()
is called on the object you add it to
the cleanup stack using CleanupClosePushL()
:void AlwaysCloseSessionL() { // Create an R object to use a server. RFs fileServerSession; // Open the session. User::LeaveIfError( fileServerSession.Connect() ); // Push the R object onto the cleanup stack. CleanupClosePushL( fileServerSession ); // Call a function that might leave. DoSomethingL(); // Clean up. CleanupStack::PopAndDestroy(); }
Close()
is called by the cleanup stack
if a leave occurs in DoSomethingL()
, and Close()
is called at the last line of the function if DoSomethingL()
does not leave. In this case CleanupStack::PopAndDestroy()
is equivalent to:// Remove the object from the cleanup stack. CleanupStack::Pop(); // Close the session. fileServerSession.Close();
As its name suggests the
cleanup stack is a last in first out container, so for any series
of pushes, the corresponding pops must occur in reverse order. As
well as removing objects individually from the cleanup stack you can
remove multiple objects in a single call to Pop()
or PopAndDestroy()
:
// Create a C object and add its address // to the cleanup stack. CExample* ptr = new (ELeave) CExample; CleanupStack::PushL( ptr ); // Create an R object and add it to the // cleanup stack. RFs fileServerSession; User::LeaveIfError( fileServerSession.Connect() ); CleanupClosePushL( fileServerSession ); ... // Remove both objects from the cleanup stack, // and call their respective cleanup functions. CleanupStack::PopAndDestroy( 2, ptr );
The call to CleanupStack::PopAndDestroy( 2, ptr )
first removes the
R object and calls Close()
on it, then removes the
C object and deletes it. The second argument is optional. It allows
you to name the last pointer being removed from the stack. In debug
builds a panic would result if this is not the same value as ptr
.