Two-phase construction

Introduction to why Symbian constructors must never leave and how Symbian achieves object initialization using constructors and second-phase ConstructL() methods.

What is new (ELeave)?

In standard C++ the new operator throws an std::bad_alloc exception if insufficient heap memory is available. It is also possible to use new (nothrow) to indicate that a null pointer should be returned instead of throwing an exception.

Symbian uses the trap and leave mechanism instead of exceptions. Accordingly new (ELeave) calls an overload of operator new() that indicates failure through a leave instead of an exception. The leave code is KErrNoMemory (-4). ELeave is a constant value of the enumeration TLeave, defined in e32const.h.

Why constructors must not leave

Consider the following code to allocate an object on the heap using new (ELeave):
CExample* ptr = new (ELeave) CExample;
This code can be broken down into 3 steps:
  1. Allocate memory. In the case of a C object derived from CBase the allocation function is TAny* CBase::operator new(TUint aSize, TLeave).

  2. Invoke the constructor. The default constructor is called in this example.

  3. Return the address of the initialized object. This is stored by the caller in the pointer ptr.

If step 1 were to fail a KErrNoMemory leave would result and control would transfer to the enclosing trap. In this case no memory has been allocated so no memory leak would occur.

What if a leave occurs at step 2 in the CExample constructor? This would cause a problem because the memory allocated at step 1 would leak. The calling code cannot be expected to resolve this problem using the cleanup stack because the address of the object has not yet been returned to the calling code.

The second phase constructor

Symbian resolves this problem by adopting the convention that a constructor must never leave. As long as the memory for the object is allocated the address will be returned successfully. The caller can then add the address of the object to the cleanup stack before performing any further initialization that may leave. This gives rise to the following idiom for object creation:
// Static member function to perform object creation
// using the two-phase construction idiom.
CExample* CExample::NewL()
  // Allocate the object. A leave could occur when
  // the memory for the object is allocated, but 
  // not as a result of the constructor call.
  CExample* self = new (ELeave) CExample;

  // Push the address of the object onto the cleanup stack.
  CleanupStack::PushL( self );

  // Perform any initialization that can leave.

  // Remove the object from the cleanup stack now that it is 
  // safely initialized.

  // Return the address of the fully initialized object.
  return self;
ConstructL() is the conventional name for the function that is invoked to carry out any initialization of the object that can leave. This function is known as the second phase constructor. ConstructL() is only called after the address of the object has been pushed onto the cleanup stack. If a leave were to occur in ConstructL() the cleanup stack would ensure that the partially constructed object is destroyed.

NewL() and NewLC()

Classes that use two-phase construction often restrict access to the constructors and ConstructL() functions by using the protected access modifier. The user instantiates the class using a public, static factory function called NewL(), as in the example above. All the arguments required to initialize the object are passed to NewL(). Inside NewL() some of these arguments are passed on to the constructor, others to ConstructL().

Note that the address returned by NewL() is no longer on the cleanup stack. If you store the address in a local variable you must push it onto the cleanup stack before it is safe to call a leaving function. For example:
  // Use the static factory function to create the
  // object. Internally this uses two-phase construction.
  CExample* ptr = CExample::NewL();

  // Push the pointer onto the cleanup stack so that it is
  // possible to call a leaving function safely.
  CleanupStack::PushL( ptr );

  // Invoke a leaving function.
Classes that define a NewL() function usually also define NewLC() as a convenience. The only difference between NewL() and NewLC() is that the address returned by NewLC() is already on the cleanup stack. So the following is equivalent to the code above:
  // Use the factory function that creates the object
  // and returns the address already on the cleanup stack.
  CExample* ptr = CExample::NewLC();

  // Invoke a leaving function.

Related concepts