Using the cleanup stack

Introduction to the basic use of the cleanup stack in Symbian code.

A leave may cause a memory leak

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.

A function that leaves can have the effect of causing a resource leak in a similar way to a standard C++ function that throws an exception. The following function illustrates this:
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.

Adding objects to the cleanup stack

You call 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();   
  }
The function has been made leave safe because the address of the heap object that is referenced only by a stack-based pointer has been pushed onto the cleanup stack before a leaving function is called. If 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
Consider the following function:
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();

Removing objects from the cleanup stack

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.

Related concepts