You Are Here:

Community: Developer Discussion Boards

Reply « Previous Thread | Next Thread »

#1 Old Why exactly ConstructL is needed? - 2008-04-30, 05:22

Join Date: Aug 2004
Posts: 289
simo.salminen
Offline
Regular Contributor
Original thread: https://developer.symbian.com/forum/...25311&tstart=0

Please post there if you have account. If not, post here, if you have answer I'll transfer it over to symbian.com afterwards.
Reply With Quote

#2 Old Re: Why exactly ConstructL is needed? - 2008-04-30, 11:29

Join Date: Mar 2003
Posts: 16,928
Location: Bangkok
symbianyucca's Avatar
symbianyucca
Offline
Forum Nokia Expert
because the consytuctor should only contain non leaving code. And this is required for efective memory cleanup. Should be explained pretty well in all books that talk about basic stuff.
Reply With Quote

#3 Old Re: Why exactly ConstructL is needed? - 2008-04-30, 19:57

Join Date: Mar 2008
Posts: 575
mahbub_s60's Avatar
mahbub_s60
Offline
Forum Nokia Expert
Hi,
Like Jukka mentioned it is described in all books. Two main points are:

C++ constructor can't return any value, we don't have any way to tell caller was it initialized successfully

Partially constructed object can be cleaned up clearly


-Mahbub
Reply With Quote

#4 Old Re: Why exactly ConstructL is needed? - 2008-04-30, 20:33

Join Date: Feb 2006
Posts: 16,371
Location: Zürich, Switzerland
wizard_hu_'s Avatar
wizard_hu_
Offline
Forum Nokia Champion
Quote:
Originally Posted by mahbub_s60 View Post
C++ constructor can't return any value, we don't have any way to tell caller was it initialized successfully
It is not that easy, neither ConstructL returns a value about success/failure. Generally the pattern appearing in the referred thread does not violate anything trivial.

The reasoning "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" sounds some kind of nonsense, and pairing the fact of CBase-derived classes are zeroed, and PushL always succeeds in Push-ing - only extending the Cleanup Stack is what may fail - nothing gets leaked, and deletion should occur.

However the post appeared from HamishW (in the referred thread) fades somehow that if multiple pushes occur (in constructors of a class-hierarchy), the multiple deletions may/will panic the code, and that is something that renders the pattern questionable. At least to me.
Last edited by wizard_hu_ : 2008-04-30 at 20:39.
Reply With Quote

#5 Old Re: Why exactly ConstructL is needed? - 2008-05-01, 14:57

Join Date: Mar 2006
Posts: 274
nigel.brown's Avatar
nigel.brown
Offline
Regular Contributor
If "iWotsit=new(ELeave) CWotsit;" leaves, won't iDoodad be orphaned?

Not that this invalidates the pattern, just that bit of code.

As far as I can see, the use of ConstructL is purely so that you can get the naming convention consistent. The constructor doesn't end in "L", so it isn't allowed to leave. This wouldn't be in issue in your own code because you would just "know" what leaves. However, it would be a bit confusing in the SDK if some constructors left and some didn't and there was no way of telling them apart.
Reply With Quote

#6 Old Re: Why exactly ConstructL is needed? - 2008-05-01, 15:01

Join Date: Nov 2005
Posts: 581
Location: London
stichbury's Avatar
stichbury
Offline
Super Contributor
I spoke to Hamish about this yesterday. We felt that the original explanation was somewhat garbled. My take is that, where you have a deep inheritance hierarchy, if you push and pop in each constructor, you end up with inefficient code. For example, if you have:

class C : public class B
{
C()
{CleanupStack::PushL(); DoLeavingStuffL(); CleanupStack::Pop()};
};

class B : public class A
{
B()
{CleanupStack::PushL(); DoLeavingStuffL(); CleanupStack::Pop()};
};


class A : public CBase
{
A()
{CleanupStack::PushL(); DoLeavingStuffL(); CleanupStack::Pop()};
};

When you construct C - you make 3 PushL() calls and 3 Pop() calls.

If you use 2 phase construction, you only push and pop once.

Furthermore, we also thought that it's more intuitive to understand. Two phase construct is an elegant concept and it's clear that it is safe. It's harder to untangle leaving code in the constructor and feel satisified that it is leave-safe (I believe it is, but aren't we aiming to write maintainable, simple code?).
Reply With Quote

#7 Old Re: Why exactly ConstructL is needed? - 2008-05-01, 15:34

Join Date: Feb 2006
Posts: 16,371
Location: Zürich, Switzerland
wizard_hu_'s Avatar
wizard_hu_
Offline
Forum Nokia Champion
I am still not satisfied with the explanation.
What happens if C() leaves? Then the Cleanup Stack will begin to release 3 objects (since the 3 PushL-s have happened). This is what I was trying to point out in my previous post.
Reply With Quote

#8 Old Re: Why exactly ConstructL is needed? - 2008-05-01, 16:01

Join Date: Feb 2006
Posts: 16,371
Location: Zürich, Switzerland
wizard_hu_'s Avatar
wizard_hu_
Offline
Forum Nokia Champion
...after having lunch...

So there are 3 overlapping objects on the Cleanup Stack - the second Free will be at least "surprised".
On the other hand there are 3 objects on the Cleanup Stack having virtual destructors, so my guess would be that all 3 destructors would be invoked for all three implied PopAndDestroys, definitely killing the code if any of them tries to delete a member variable (quite common for destructors), but does not NULL it (also quite common for destructors, since NULL-ing variables that will not be accessed again - since the object is deleted - is just a waste of code).
Reply With Quote

#9 Old Re: Why exactly ConstructL is needed? - 2008-05-01, 19:53

Join Date: Jul 2004
Posts: 1,757
hotcheese
Offline
Forum Nokia Champion
[quote=nigel.brown;412405]
If "iWotsit=new(ELeave) CWotsit;" leaves, won't iDoodad be orphaned?
QUOTE]


As 'this' is being pushed to the cleanupstack, if it leaves CWidget's destructor will be called.
Last edited by hotcheese : 2008-05-01 at 20:29.
Reply With Quote

#10 Old Re: Why exactly ConstructL is needed? - 2008-05-01, 22:42

Join Date: Aug 2004
Posts: 289
simo.salminen
Offline
Regular Contributor
Thanks all for replies. Trying to get a bottom of this, so did an example. Plot thickens. See below.


This code leaves at C::C(). I put breakpoints in the code, and see that for example C::C() -> C::~C() -> B::~B() -> A::~A(). As expected.

However, after this, there is call C::C() -> B::~B(), thus causing double deletion (USER 42). When debugging, crash does not happen every time, although most of the time.

Why this happens, any ideas?

wizard_hu, I can't see how the cleanupstack could contain the A , B and C this pointers at same time. Each ctor pushes and pops without interfering with others. For example when C::C() pushes 'this', the B and C have already been run (pushed and popped). Right? Remember that base ctors are executed before any code in current class ctor.


Code:
class A : public CBase
{
public:
A()
{
CleanupStack::PushL(this); 
iMa = User::AllocL(10);
CleanupStack::Pop(this);
};

virtual ~A()
	{
	delete iMa;
	}
	TAny* iMa;
};

class B : public A
{
public:
B()
{
CleanupStack::PushL(this); 
iMb = User::AllocL(10);
CleanupStack::Pop(this);
};

virtual ~B()
	{
	delete iMb;
	}
	TAny* iMb;
};

class C : public B
{
public:
C()
{
CleanupStack::PushL(this);
iMc = User::AllocL(10);
User::Leave(-666);
CleanupStack::Pop(this);
};

virtual ~C()
	{
	delete iMc;
	}
	TAny* iMc;
};
Reply With Quote

#11 Old Re: Why exactly ConstructL is needed? - 2008-05-02, 00:01

Join Date: Feb 2006
Posts: 16,371
Location: Zürich, Switzerland
wizard_hu_'s Avatar
wizard_hu_
Offline
Forum Nokia Champion
Quote:
Originally Posted by simo.salminen View Post
wizard_hu, I can't see how the cleanupstack could contain the A , B and C this pointers at same time. Each ctor pushes and pops without interfering with others.
You are totally right, I was writing pure nonsense :-(
Quote:
However, after this, there is call C::C() -> B::~B(), thus causing double deletion (USER 42). When debugging, crash does not happen every time, although most of the time.

Why this happens, any ideas?
If you check the call stack, you will see that C::C() remains there during the whole Cleanup Stack-process. So the first deletion comes from the Cleanup Stack, and the second attempt probably comes from some C++-related mechanism (that is probably why the current class - C - is not included).

Perhaps this pattern is still not that good anyway ;-)
Reply With Quote

#12 Old Re: Why exactly ConstructL is needed? - 2008-05-02, 09:53

Join Date: Aug 2003
Posts: 1,005
Location: Oulu, Finland
laa-laa's Avatar
laa-laa
Offline
Forum Nokia Champion
Quote:
Originally Posted by wizard_hu_ View Post
So the first deletion comes from the Cleanup Stack, and the second attempt probably comes from some C++-related mechanism (that is probably why the current class - C - is not included).
Yes - nowadays leaves are C++ exceptions internally and not based on setjmp() and longjmp(). When an exception is thrown in a constructor, the destructor is not called since the object has not been constructed yet. However, the superclass parts of the object have been constructed and their destructors are called.

Lauri
Reply With Quote

#13 Old Re: Why exactly ConstructL is needed? - 2008-05-02, 10:51

Join Date: Mar 2006
Posts: 274
nigel.brown's Avatar
nigel.brown
Offline
Regular Contributor
Good point.
Won't it cause some potential problems if the cleanupstack tries to delete a partially formed object?
Reply With Quote

#14 Old Re: Why exactly ConstructL is needed? - 2008-05-02, 11:38

Join Date: Feb 2006
Posts: 16,371
Location: Zürich, Switzerland
wizard_hu_'s Avatar
wizard_hu_
Offline
Forum Nokia Champion
Yes, I just woke up, and wanted to write what Laa-laa did. So it is not "some C++ mechanism", but the standard behaviour (destructors are called for fully constructed superclasses), and according to some sources (http://www.cs.technion.ac.il/~imaman...owingctor.html) "Initialization exceptions cannot be hidden". Yesterday when I added zeroing+check in the destructors (so deletion iMx-s did not occur twice), User 42 still appeared, since the object itself was still Free-ed twice.
It also means that the construct in question was safe in older Symbian releases (like in its original source - some 92xx-SDK related paper), since that time Leave-s were working independently from C++ exception handling.
Reply With Quote

#15 Old Re: Why exactly ConstructL is needed? - 2008-05-02, 12:47

Join Date: Nov 2005
Posts: 581
Location: London
stichbury's Avatar
stichbury
Offline
Super Contributor
So, to summarize, I think we're saying:

Don't leave in a constructor - use 2-phase construction if you need to.

In Symbian OS v9, this is for an additional reason than the (rather obscure) explanations offered (as quoted in Simo's post).

Correct? Just thought I'd put this here to help future readers of the thread.
Reply With Quote
Reply « Previous Thread | Next Thread »
Display Modes
Thread Tools Search this Thread
Search this Thread:

Advanced Search

Posting Rules

You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

vB code is On
Smilies are Off
[IMG] code is On
HTML code is Off
Forum Jump
Similar Threads
Thread Thread Starter Forum Replies Last Post
Form not displayed when is created in Container ConstructL redbart Symbian User Interface 2 2008-04-01 11:30
Tool to find the needed capabilities gaurav C Symbian Tools & SDKs 4 2008-03-25 13:30
beginner's question:difference between ConstructL and BaseConstructL 021850524 General Symbian C++ 3 2007-07-19 10:54
How to set number for forwarding?? silviuccia General Symbian C++ 6 2007-01-09 11:16
help needed rajiv_614 General Browsing 2 2006-04-17 12:56

Rate This

 
Bookmark this page: DeliciousDiggFacebookGoogleYahooStumbleUponRedditDiigoTechnocratiTwitter  Share this page Share this page Print this Page Print this page Invite a friend Invite a friend
京ICP备05048969号    Email Newsletters Press Terms & Conditions Privacy Policy Sitemap Contact Us © 2009 Nokia