RPointerArray and CleanupStack
| Tue, 2008-04-08 14:44 | |
|
Hello All, I am trying to understand how the RPointerArray works I have written a piece of code as below and it is working perfectly fine. RPointerArray<CMyClass>* CUtils::CreateRpointer(TInt Count)Now, I would like to add the cleanup stack functionality into my RPointerArray pointer and the other pointers that I appended into that. I dont know how to write the cleanup stack functionality for the heap allocated memory in the function CreateRPointer can somebody help me in doing that with a piece of code ? Regards |
|






Forum posts: 132
'R' objects are not intended to be allocated on the heap, first of all rewrite it so that your array is on the stack.
Forum posts: 6
thanks for the reply..
In my project, I want the allocation to be happened on the heap which makes the implementation more elegent and suitable in the context.. that is the reason I allocated it on the heap..
Is there any work around for this ?
Forum posts: 132
How does allocating an R object on the heap make it more elegant or suitable? Do you want to create it in one function and return ownership? The usual way is to create it in the calling function and pass it as a parameter to be populated.
Creating R objects on the heap usually makes cleanup more complex.
You might need to use a TCleanupItem.
Forum posts: 6
Yes, I want to create the RPointerArray in a function and pass the ownership to the called functions. In my project, the APIs are already defined that way.. I am not in a position to change the function signature now
In some other thread, I have seen that we can use the cleanupstack for RPointerArray in the following way.. Is it a correct implementation
RPointerArray<CMyClass> *pMyArray = new (ELeave)RPointerArray<CMyClass>();
CleanupResetAndDestroyPushL( pMyArray);
/* Add the items into the RPointerArray here */
CleanupStack::Pop();
return pMyArray;
Also Can you please explain me how to use TCleanupItem ?
Thanks
Mac
Forum posts: 24
Hi,
You are absolutely right, there is no such restriction on RPointerArray that it has to be allocated on to the stack.
Now for cleanup purpose, CleanupStack provides 3 variants of PushL(). One of the variant takes TCleanupItem. For instance in your case the RPointerArray cannot simply be deleted(since we need to call Reset on it and delete all the pointers it contains), so you ve use this overloaded version of PushL (TCleanupItem aItem).This method allows us to define our own cleanup operation.The following code ll make you clear
void CleanupResetAndDestroy(TAny* aItem){
RPointerArray<CMyClass> *pMyArray = (RPointerArray<CMyClass>*) aItem;
pMyArray.ResetAndDestroy();
pMyArray.Close();
delete pMyArray;
//or u can use free if u dont ve any destructor
}
RPointerArray<CMyClass>* CUtils::CreateRpointer(TInt Count)
{
RPointerArray<CMyClass> *pMyArray = new (ELeave)RPointerArray<CMyClass>();
CleanupStack::PushL(TCleanupItem(CleanupResetAndDestroy(pMyArray ));
for(TInt ct = 0; ct < Count; ct++)
{
CMyClass *myClass = CMyClass::NewL(ct);
(*pMyArray).AppendL(myClass);
}
return pMyArray;
CleanupStack::Pop();
}
The CleanupResetAndDestroy operation is called when the subsequent call to CleanupStack::PopAndDestroy is made
Now Suppose the NewL of CMyClass leaves, CleanupResetAndDestroy is called and the cleanup is done.
This operation is more useful where u ve opened several resources and u need to cleanup.
I hope this is clear
Forum posts: 587
But it will make things more complex. As R classes are designed not to be dynamically allocated, why do it when you can live without it?!
Often novices make the mistake of allocating R objects from heap and then just deleting it. This will cause problems since they do not remember to call Close (Reset/ResetAndDestroy/whatever) needed to really release the resources allocated by the R object. delete does not do this and R destructors usually do not do this. You need to call Close explicitly.
So just make your life easier, do not allocate R on heap but on stack, call Close (whatever) in the end and that's it. Furthermore, if your R object is actually a member variable of a C class, the memory used by the R object is actually on the heap already. So this does not save stack memory. The only place where stack is saved is when you use the R object inside a method temporarily. Finally, R classes do not often spend a lot of memory anyways, since the actual work with the actual data is done somewhere else, in a server usually.
Forum posts: 1134
I can't remember having my RPointerArrays on the stack, ever.
I keep them as a member variable of a CBase object, thus... on the heap.
Though, you are right you shouldn't use new explicitly to create it....
Just wanted to confuse everyone some more
Forum posts: 132
Alh you're right you've confused them even more.
That unchangeable function signature is incorrect by the way, there is no trailing L in its name to indicate it is a leaving function. And there is a memory leak if AppendL() leaves. So if that function can't be changed then whats the point of making the calling code leave safe if the function its calling isn't itself leave safe?
Forum posts: 6
hello guys,
I am totally lost now..
From the discussion we had here, I understood that there is nothing wrong in allocating RPOinterArray on stack or heap.. Only thing is that we should be cautious to call ResetAndDestroy, Close on RPointerArray.. Also we need to delete the RPointerArray Pointer.. Is that my understanding correct ??
And there are basically 2 ways to implement the cleanupStack. 1/ CleanupResetAndDestroyPushL and 2/ TCleanUpItem. which one is the preferred solution ?
Just to summarize, I will put both the solution here..
1/CleanupResetAndDestroyPushL
#include "mmfcontrollerpluginresolver.h"
RPointerArray<CMyClass>* CUtils::CreateRpointerL(TInt Count)
{
RPointerArray<CMyClass> *pMyArray = new (ELeave)RPointerArray<CMyClass>();
CleanupResetAndDestroyPushL( pMyArray);
for(TInt ct = 0; ct < Count; ct++)
{
CMyClass *myClass = CMyClass::NewL(ct);
CleanupStack::PushL(myClass);
(*pMyArray).AppendL(myClass);
CleanupStack::Pop();
}
CleanupStack::Pop();
return pMyArray;
}
2/ Using TCleanupItem
void CleanupResetAndDestroy(TAny* aItem){
RPointerArray<CMyClass> *pMyArray = (RPointerArray<CMyClass>*) aItem;
pMyArray->ResetAndDestroy();
pMyArray->Close();
delete pMyArray;
}
RPointerArray<CMyClass>* CUtils::CreateRpointerL(TInt Count)
{
RPointerArray<CMyClass> *pMyArray = new (ELeave)RPointerArray<CMyClass>();
CleanupStack::PushL(TCleanupItem(CleanupResetAndDestroy(pMyArray ));
for(TInt ct = 0; ct < Count; ct++)
{
CMyClass *myClass = CMyClass::NewL(ct);
CleanupStack::PushL(myClass);
(*pMyArray).AppendL(myClass);
CleanupStack::Pop();
}
return pMyArray;
CleanupStack::Pop();
}
Thanks
Mac
Forum posts: 24
Hi,
To make every one clear it is not at all a strict convention to allocate a R based object on stack, even the R object itself uses heap internally when u call Append or insert in case of
.
RPointerArray so no confusion about stack or heap both work
Now
mac123
As you can see the method
CleanupResetAndDestroyPushL( pMyArray); is in #include "mmfcontrollerpluginresolver.h" which is a specific to MMF
so it is as good as saying that MMF has written its own cleanp operation CleanupResetAndDestroyPushL( pMyArray);
So I think internally he is also using the same TCleanupItem solution which i gave you.
It is always better to ve your own cleanup operation.
So just use the second one to better understanding and correct cleanup
.
Forum posts: 1058
I qoute from Andreas' post:
You can continue to hit your head with a hammer, but why should you?
René Brunner
Forum posts: 1134
Well, imho, what is confusing is everyone reiterating this falsehood about "R-classes are supposed to live on the stack".
Its just not true at all, and just very confusing for people trying to understand how it really works.
What is true though is that they are _not_ supposed to be owned via a pointer, and you should _not_ use new to create them.
But that does _not_ mean "they should be on the stack"....
Contrary to CBase objects they _can_ be put on the stack, but that is very far from "they have to be on the stack".
But... just to be extra clear:
You should go to your architect or whoever decided that function signature and tell him it is a bad design, and why, and convince him to change it.
It has a faulty design, and will create a lot of extra work for you (which you have already started to notice) and probably be a source of a few defects...
Forum posts: 587
OK, OK I confess being too vague about the R -classes. You do not need to "allocate them on the stack" but as already said, you usually shouldn't dynamically allocate them (though you can; see e.g. Babin: Developing Software for Symbian OS, 2nd Ed, p. 76).
What your example here does is that it actually fills a RPointerArray with objects for the caller. Why not avoid the dynamic allocation of R object like this:
void CUtils::FillArrayL(TInt aCount, RPointerArray<CMyClass> & aArray){
aArray.ResetAndDestroy(); // Optional if the array should be emptied first. Caller could do this too.
CleanupResetAndDestroyPushL(aArray);
for(TInt ct = 0; ct < Count; ct++)
{
CMyClass *myClass = CMyClass::NewL(ct);
CleanupStack::PushL(myClass);
aArray.AppendL(myClass);
CleanupStack::Pop();
}
CleanupStack::Pop(); // Thanks, alh, for pointing out this one.
}
Now you do not have to do dynamic allocation in order to return a local object from the method. Let the caller take care of the construction of the array object.
void CUtils::UseArrayL()
{
RPointerArray<CMyClass> myArray;
FillArrayL(10, myArray);
/* do something, and if it can leave, push myArray to cleanup stack again! */
mArray.ResetAndDestroy();
}
See, no need to dynamically allocate RPointerArray, much simpler code to use.
Forum posts: 1134
Just one note, the FillArrayL in andreas post should be named FillArrayLC (since it leaves a cleanupitem on the cleanupstack)
And you would have to do either:
or simply:
to empty it in the end of UseArrayL
Forum posts: 587
Or do as I fixed, pop it out in the FillArrayL as I originally intented. Fixed it to the code. Optionally do as alh said.