| Reply | « Previous Thread | Next Thread » |
|
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. |
| simo.salminen |
| View Public Profile |
| Find all posts by simo.salminen |
|
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.
|
|
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 |
| mahbub_s60 |
| View Public Profile |
| Find all posts by mahbub_s60 |
|
Join Date: Feb 2006
Posts: 16,442
Location: Zürich, Switzerland
Offline
Forum Nokia Champion
|
|
|
Quote:
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.
|
| wizard_hu_ |
| View Public Profile |
| Find all posts by wizard_hu_ |
|
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. |
| nigel.brown |
| View Public Profile |
| Find all posts by nigel.brown |
|
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?). |
|
Join Date: Feb 2006
Posts: 16,442
Location: Zürich, Switzerland
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. |
| wizard_hu_ |
| View Public Profile |
| Find all posts by wizard_hu_ |
|
Join Date: Feb 2006
Posts: 16,442
Location: Zürich, Switzerland
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). |
| wizard_hu_ |
| View Public Profile |
| Find all posts by wizard_hu_ |
|
[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.
|
|
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;
};
|
| simo.salminen |
| View Public Profile |
| Find all posts by simo.salminen |
|
Join Date: Feb 2006
Posts: 16,442
Location: Zürich, Switzerland
Offline
Forum Nokia Champion
|
||
|
Quote:
Quote:
Perhaps this pattern is still not that good anyway ;-) |
| wizard_hu_ |
| View Public Profile |
| Find all posts by wizard_hu_ |
|
Quote:
Lauri |
|
Good point.
Won't it cause some potential problems if the cleanupstack tries to delete a partially formed object? |
| nigel.brown |
| View Public Profile |
| Find all posts by nigel.brown |
|
Join Date: Feb 2006
Posts: 16,442
Location: Zürich, Switzerland
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. |
| wizard_hu_ |
| View Public Profile |
| Find all posts by wizard_hu_ |
|
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 | « Previous Thread | Next Thread » |
| Thread Tools | Search this Thread |
|---|---|
| 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 |