C++ and 2-phase construction query

Login to reply to this topic.
Wed, 2006-05-24 01:06
Joined: 2006-05-09
Forum posts: 78
Hi

If the reason for 2-phase construction is so that if a constructor fails and leaves then its to prevent a memory leak of the object being constructed i.e. if A has a class B:

CA* A = new(ELeave) A;

CA::CA()
{
iB = new(ELeave) B;
}

Then in B fails to allocate, the constructor leaves and the memory on the heap for A is orphaned.
However what happens in the analogous situation with standard C++? If instead of leaving an exception is thrown then does the memory for A automatically get cleaned up?

Wed, 2006-05-24 07:41
Joined: 2006-05-22
Forum posts: 37
Re: C++ and 2-phase construction query
You are not allowed to leave in a default constructor.

CA::CA()
{
iB = new(ELeave) B; <--- Not allowed because it can leave!
}

Exactly for that purpose you have the two phase constructor with default constructor NewL and a ConstructL functions.

have fun...

Rayko Enz
www.sic-software.com

Wed, 2006-05-24 11:28
Joined: 2005-11-20
Forum posts: 1157
Re: C++ and 2-phase construction query
I tried to answer the specific question "In the described scenario of a constructor throwing an exception, would already the C++ exception mechanism alone guarantee that there will be no memory leaks?"

When I started Googling, I found texts like these:
http://public.research.att.com/~bs/3rd_safe.pdf
http://www.accu-usa.org/2001-03-Main.html
that I did not go through in detail and would not completely understand anyway, I'm afraid.

But I did definitely not get the impression that the C++ exception mechanism alone already prevents memory leaks in all situations if you allow constructors (and destructors) to throw exceptions that you catch only "outside" i.e. not in the constructors and destructors itself.

So I think the Symbian way of doing things "by the hand" with 2-phase construction and leaves may be less elegant than C++ exceptions, and not "standard" as well of course, but at least everything is so simple that one can easily understand the rules and also easily understand what is going on.

René Brunner

Thu, 2006-05-25 09:59
Joined: 2006-01-09
Forum posts: 105
Re: C++ and 2-phase construction query
Hi

actually symbian C++ is used only in devices having much less memory as compare to desktop computers.
so memory allocation failure can occur frequently as compare to desktop.

it is frequent to occur OOM (Out Of Memory) errors.its all because of less memory/resources available in hand held devices.

that's why they had to invent a new rule to overcome these kind of Errors..



Enjoy!!
CodePupil

Thanks and Regards
CodePupil
__________________________
You are I and I am he .. !!

Fri, 2006-05-26 10:12
Joined: 2004-06-11
Forum posts: 23
Re: C++ and 2-phase construction query
If an exception is thrown in a C++ constructor for a locally declared object the destructor for the object will not be called.  This is because the object has not been fully constructed and C++ will never call a destructor for a partially constructed object, e.g.,

Code:
class B;

class A
{
public:
    A()
    {
        iB2 = new B;
        iB3 = new B; //throws an exception;
    }
    ~A() { delete iB2; delete iB3;}

    B iB1;
    B* iB2;
    B* iB3;
};

void fn()
{
    A a;  // calls A::A() which throws an exception.
            // A::~A will not be called and iB2 will be leaked
}

The interesting thing is that C++ will call destructors for any members of the partially constructed object that themselves have been fully constructed.  So in the above example the destructor B::~B will be called for iB1.

So this begs the question how do I throw exceptions from constructors without incurring memory leaks.   The answer is to use auto_ptrs.  So you should rewrite class A as follows.

Code:
class B;

class A
{
public:
    A() : iB2(new B), iB3(new B)
    {
    }

    B iB1;
    const auto_ptr<B> iB2;
    const auto_ptr<B> iB3;
};


Now if the second call to new B fails then, the destructor A::~A will not be called but the destructors for iB1 and iB2 will be called as they have been fully constructed.  The destructor for iB2 deletes what it points to, i.e., an instance of B and hence there is no memory leak.

All of this is really well explained in Scott Meyers More Effective C++.

Finally, you should not throw an exception from a destructor because the destructor could have been called as a result of another exception having being thrown.  This means two exceptions are now actively being thrown and this is not allowed.  The C++ runtime calls terminate and terminate, I think,  calls abort and that's the end of your program.
Fri, 2006-05-26 10:44
Joined: 2004-06-11
Forum posts: 23
Re: C++ and 2-phase construction query
I should have mentioned that we cannot use auto_ptrs in versions of Symbian < 9 as TRAPs and Leaves are implemented using setjmp and lngjmp which are C constructs that know nothing about C++ destructors.  For this reason when a Leave is thrown destructors for locally declared objects are not called as the stack unwinds.  This is why we have T and C classes and the cleanup stack. 

However, in Symbian 9, TRAPs and Leaves are implemented using C++ exceptions.  Hence, we can use auto_ptrs and strictly speaking , I guess there is no need to use the cleanupstack in your own classes to avoid resource leaks.  However, I guess the OS itself still uses the cleanupstack and it is probably not a good idea to mix cleanup idioms, so its probably best to continue to use the cleanup stack unless you are porting C++ code from a non Symbian platform.  In addition if you want to retain compatibility with earlier versions of the OS you will need to use the cleanup stack.
Fri, 2008-06-27 11:28
Joined: 2006-12-19
Forum posts: 72
Leaving of C++ contructor and Two phase constructor...

Sorry Guys, still hv some doubt.

For the code below,

CA* A = new(ELeave) A;

CA::CA()
{
iB = new(ELeave) B;
}

Fructose has written ,

Then in B fails to allocate, the constructor leaves and the memory on the heap for A is orphaned.

So, doesn't C++ or Symbian C++ take responsibility to collect the memory allocated to A while its own constructor leaves?
Do programmer need to throw exception in standard C++ or Two-phase constructor is needed in Symbian C++ for this purpose ? Please , make me bit clear abt it.

Now what I understood- is we need two-phase constructor in the following scenario.

class CAClass
{
//
B* b;
C* C;
}

If "CAClass* aClass=new (ELeave) CAClass" leaves, after allocating memory for '" b " and while allocating that for C,then
memory for b on heap becomes orphaned.So we need 2-phse ctor.But if we have only one member in CAClass and the ctor fails allocating it, then we dont need a 2-phase ctor as memory has not been allocated to b and memory allocated to CAClass will be claimed automatically(not sure).

Pls, help me being clear about the concept.

Regards,
N

Fri, 2008-06-27 11:45
Joined: 2006-12-19
Forum posts: 72
Re: C++ and 2-phase construction query

here i got the answer.

Class CAClass will be contructed some where in side any other class as :

CAClass* aClass = new (ELeave) CAClass ;

So when its ctor fails, only the pointer pointer to the oject will be deleted and not the data pointed by it.ie. memeory allocated to CAClass object is orphned.If it were a stack based data , it would have been deleted automatically.

Fri, 2008-06-27 11:50
Joined: 2003-12-05
Forum posts: 622
Re: C++ and 2-phase construction query

This wouldn't cause a problem:

class CA : public CBase {
public:
   CA()
      {
      iX = new (ELeave) CX;
     }
     ~CA()
     {
     delete iX;
     }
private:
    CX * iX;
};

Since if allocation of iX would fail, nothing actually would have been allocated at all. So no need for two phase construction However, this would cause a leak:
class CA : public CBase {
public:
   CA()
      {
      iX = new (ELeave) CX;
      iY = new (ELeave) CY; // OOM here
     }
     ~CA()
     {
     delete iX;
     delete iY;
     }
private:
    CX * iX;
    CY * iY;
};

Since iX would have been allocated and cannot be deallocated since object of class CA was not constructed fully, thus cannot be deleted.

That's why the two phase construction, which would solve the problem. Another way to avoid this leak would be to use the auto_ptr as already said, for example like this:

class CA : public CBase {
public:
   CA()
      {
      auto_ptr<CX> ptrX(new (ELeave) CX);
      auto_ptr<CY> ptrY(new (ELeave) CY);
     iX = ptrX.release();
     iY = ptrY.release();
     }
     ~CA()
     {
     delete iX;
     delete iY;
     }
private:
    CX * iX;
    CY * iY;
};

Now, if either of the allocations would fail, the stack based auto pointer destructors would be called and the auto_ptr's would delete their data. If the leave wouldn't happen, then the object under construction would take the ownership of the pointers.

Tue, 2008-07-01 07:06
Joined: 2006-12-19
Forum posts: 72
Re: C++ and 2-phase construction query

Thanks Andreas,

Here exactly I was unsure.Now its clear to me.

Cheers,
N

Fri, 2008-08-01 17:39
Joined: 2006-12-19
Forum posts: 72
Re: C++ and 2-phase construction query

Hi,

I was thinking that when a comppound class is having a pointer to single simple object to be allocated on heap,
we do not need two-phase construction.As told by Andeas in above posts, when the simple class's contructor leaves
nothing has been allocated to compound class.

but its not the case I guess.Because new (ELeave ) CCompoundClass has would have allocated memory to it.And here also we will get memory leak.
Here is an exract from http://www3.symbian.com/faq.nsf/0/33B0DD83F45F197980256A570051B93B?OpenDocument

Here’s the definition for CMyCompoundClass:

  class CMyCompoundClass : public CBase

            {

            public:

            CMyCompoundClass();

            ~CMyCompoundClass();

            …

            private:

            CMySimpleClass * iSimpleClass; // owns another C-class

            };

Now ,

CMyCompoundClass* myCompoundClass=new (ELeave) CMyCompoundClass;

....................
..................
If we make the constructor leave, we can detect when the object was not fully constructed, as follows:

CMyCompoundClass::CMyCompoundClass() // WRONG

{

iSimpleClass=new (ELeave) CMySimpleClass;

}

However, this is not a viable method for indicating that an error has occurred, because we have already allocated memory for the CMyCompoundClass. A leave would destroy the pointer (this) to the allocated memory, and there would be no way to free it, resulting in a memory leak.

The solution is to allocate all the memory for the components of the object, after the compound object has been initialized by the C++ constructor. By convention, in SYMBIAN OS this is done in a ConstructL() function.

Does not this mean that even if we have a single simple object to be allocated on the heap we need two phase construction?

Mon, 2008-08-04 21:30
Joined: 2003-12-05
Forum posts: 622
Re: C++ and 2-phase construction query

Yes, I guess so, since the constructor for the CBase has already been successfully called, as well as the initialization of the compound class has been successfully done (before entering the actual code block of the constructor). So I was wrong and you need to use 2 phase construction also in this case. Somebody more intimate with C++ constructors may need to confirm this.

  • Login to reply to this topic.