Support

Symbian Developer Network C++ Code Clinic

June 2008: Can I Leave in a Constructor?

Code Clinic

Jo Stichbury re-opens the code clinic to dispense more advice about Symbian C++.

In this month's clinic she examines why it's a bad idea to leave in a constructor, and why the two-phase construction idiom should be preferred.

This week I'm going to elaborate on a question that was raised on the Symbian Developer Network discussion forums at the end of April. User Simo Salminen queried some legacy documentation that stated that it was inadvisable to include leaving code in a constructor [R1].

First, let's take the simplest explanation of why it's a bad idea to leave in a constructor. Take the following line of code, which allocates an object of type CWidget on the heap:

CWidget* widget = new (ELeave) CWidget();

When it executes, a CWidget object is first allocated on the heap if there is sufficient memory available (if there is not, a leave occurs and no construction takes place). Assuming that CWidget was allocated successfully on the heap, the constructor of class CWidget is then called to initialize the object in place. If the constructor leaves, any other memory the constructor may have already successfully allocated will be orphaned, because the destructor is not called if an exception occurs during construction. This results in a memory leak.

On Symbian OS, the two-phase construction idiom is most commonly used to allow initialization code that may leave to be called safely [R2]. Using two-phase construction, the CWidget class is defined as follows:

class CWidget : public CBase
  {
public:
  static CWidget* NewL();
  static CWidget* NewLC();
  ~CWidget();// Must cope with partially constructed objects
private:
  CWidget(); // Guaranteed not to leave
  void ConstructL(); // Second phase construction code, may leave
  ...
  };

This should be familiar to most Symbian C++ developers, for whom two-phase construction for C classes is second nature. However, the documentation Simo Salminen discovered took the discussion a step further. Instead of adding static factory functions, a private ConstructL() and the boilerplate code associated with two-phase construction, it suggested, what about just using the cleanup stack inside the CWidget constructor instead?

CWidget::CWidget()
  {
  CleanupStack::PushL(this);
  // Call leaving code here
  CleanupStack::Pop(this);
  }

Now any memory allocated to CWidget() is protected by the cleanup stack should a leave occur, thus avoiding a memory leak. It's certainly simpler to implement than the static NewL() and NewLC() factory functions, and separate, private ConstructL() method. But the documentation wasn't particularly encouraging, stating, "this might work in some situations, but not in others, because of lifetime issues during the constructor, and rules about the order of base class construction". This is rather vague, but I have to admit that I also 'fudged' the explanation of why it wasn't a great idea when I wrote Symbian OS Explained back in 2004. I said "At first sight, this may be tempting, since...[it]...would then be leave-safe, as long as the object was pushed onto the cleanup stack before any leaving operations were called in the constructor. However, if the class is to be used as a base class, the constructor of any class derived from it will incur one PushL() (and corresponding Pop()) in the constructor called at each level in the inheritance hierarchy, rather than a single cleanup stack operation in the NewL() function. In addition, from a cosmetic point of view, a C++ constructor cannot be marked with a suffixed L to indicate its potential to leave unless the class is itself named as such". In retrospect, this is a reasonable explanation, but the performance gains to avoiding multiple PushL()/Pop() pairs aren't massive, except for very deep inheritance hierarchies, and the amount of extra code generated from two-phase construction in every C class must have been a headache at a time when ROM was limited.

So what's the real reason not to take the short cut and prefer two-phase construction? After Simo posted the original question, the developer community on Forum Nokia discussed what would happen if CWidget() was implemented as I've shown it above, using the cleanup stack in place of two-phase construction [R3]. It became clear that, while we couldn't see any major issues arising at the time Symbian OS was designed, there are significant issues now that Symbian OS implements leaves in terms of standard C++ exceptions. Consider the following:

CWidget::CWidget()
  {
  CleanupStack::PushL(this);
  InitializeL();
  CleanupStack::Pop(this);
  }	

If InitializeL() leaves, the following sequence occurs (see [R4] for further details).

  1. The cleanup stack 'unwinds' and destroys the object (explicitly calling CWidget::~CWidget() and then freeing the memory allocated for the object.
  2. A XLeaveException exception is created and thrown to represent the leave, the stack is unwound and exception is caught.
As Lauri Aalto, one of the commenters on the discussion boards explains, when an exception is thrown in a constructor, the destructor of that object is not called since the object has not yet been constructed. However, any superclass parts of the object have been constructed so their destructors are called and they are freed back to the heap. So if CWidget derives from CGadget, the CGadget destructor is called twice - first by the cleanup stack and then as a result of the exception. The second time the destructor is invoked, it is on an object that has already been destroyed. If the destructor attempts to access any member variables it causes a memory fault, which results in a USER 42 panic (“This panic is raised by a number of RHeap member functions...when a pointer passed to these functions does not point to a valid cell.”)
class CGadget : public CBase
  {
public:
  CGadget();
  ~CGadget() {delete iBuf};
private:
  HBufC* iBuf;
  };

class CWidget : public CGadget
  {
public:
  CWidget();
  ~CWidget() {};
private:
  void InitializeL();
  };

Summary

Now that Symbian OS leaves are implemented in terms of C++ exceptions, we have a strong explanation for why two-phase construction should be preferred, instead of simply using the cleanup stack to prevent memory leaks inside a constructor that may leave. The reason uncovered by the discussion thread is that it prevents potential double deletions. While it wasn't an issue when leaves were implemented in terms of setjmp and longjmp, it is now that C++ exceptions are used. This is because C++ guarantees to call the destructor of any fully-constructed superclasses in the event of an exception. If a class derives from other classes which have explicit and implemented destructors you must defer calling any leaving code until after the standard C++ constructor has completed. Or, put simply, just keep using two-phase construction to ensure leaving code is called after the constructor has fully executed.

Another reason for preferring the two-phase construction idiom is that the use of a static factory function that returns a pointer to fully instantiated object, allows the C class constructor to be kept private. This prevents accidental instantiation of C class objects on the stack, and also allows the size of the class to be modified without breaking binary compatibility (you can find out more about this in Chapter 18 of Symbian OS Explained). Personally, I also think two-phase construction is just more intuitive. It's easier to understand what will happen if a leave occurs during object instantiation and initialization, than if you do it all in the constructor.

The original poster wrote a nice summary in which he suggests that the original use of two-phase construction allowed Symbian OS to be future proof against the introduction of exception handling.

Further Discussion

The implementation of leaves in terms of C++ exceptions has a number of additional consequences, discussed in more detail in [R4]. And next month I'll go through the pitfalls of allowing code to leave inside a destructor.

References

[R1] "Why exactly is ConstructL() needed? " on Symbian Developer Network forums, April 2008.

[R2] Two-Phase Construction in the Symbian OS Guide of the Symbian Developer Library.

[R3] "Why exactly is ConstructL() needed? " on Forum Nokia Developer Discussion Boards April/May 2008.

[R4] "Leaves and Exceptions ", Jason Morley, Symbian Developer Network, 2007.

Contributors

Thanks to Lauri Aalto, Will Bamberg, Tanzim Husain and Hamish Willee who provided feedback on aspects of this article, and to user Simo Saminen, for initiating the discussion on the discussion forums at developer.symbian.com.

The Code Clinic will feature regularly on Symbian Developer Network, on the first Friday of every month.

Got a Symbian C++ problem? Send it to the Code Doctor at SDN@symbian.com. Problems used will receive a complementary copy of the Symbian Press book "Developing Software for Symbian OS, Second Edition".

 
[live-web] Terms of use | Privacy policy | Media Center | Contact us | © 2008 Symbian