Here are some reminders about C type classes for a typical question I saw on NewLC
Why does my C class not initialize the members to zero? Is this a bug?
1. Do not create C classes on the stack. This is the reason the member variables do not get set to zero.
2. C classes allocate dynamic memory. This will result in leaks if any of the methods following leave.
3. C classes almost always do two phase construction. This is where the resources are allocated and at this point can leak if the object is not on the cleanup stack
4. R classes allocate handles. - These handles may be leaked if any of the methods following the Open method leave. Be very careful if you open a handle and do not put it on the cleanup stack! People put returns in the middle of the function and forget to handle functions that leave.
5. Programming standards in Symbian are paramount. -If the class allocates memory, derive it from CBase and put it on the cleanup stack. -If it is using handles, use an R class and put it on the cleanup stack See the Symbian Wiki for more information on standards
6. Follow Scott Meyers advice and make your constructors and ConstructL class private. -So that the class cannot be inadvertantly created on the stack. -Make you copy constructor and assignment operator private and unimplemented so you get compile errors if you accidentally use them. -Always use NewLC or NewLC to force people to use the proper constructor Effective C++: 50 Specific Ways to Improve your Programs and Design
7. Put the address of the item you expect to be at the top of the stack in the argument for PopAndDestroy There are overloads on PopAndDestroy that allow you to provide a pointer for the item you expect to be on the top of the stack after popping and destroying items. You will get a panic if this item is not at the top of the stack after the items have been popped.
One more thing to add: use the right order of classes that you derive from in your class declaration. For example, one can assume that if your class is derived from CBase, then some M... classes it would be the same as M... classes then CBase. Of course, any C class will do instead of CBase.
Well, it's not the same. A typical use case is that you have to write the implementation of a mixin class (e.g. an observer) - and you have to make it so that your class also derives from CBase. If you change the "recommended" order so that CBase is not at the first place in the order of declaration which class your class derives from, then the following code won't work:
class CSomething : public MSomeObserver, public CBase { ... }
CSomething* sg = CSomething::NewLC(); CleanupStack::PopAndDestroy( sg ); // <-- it will panic!!!
The reason why PopAndDestroy() panics in the above line is that the pointer we've pushed on to the cleanup stack is NOT THE SAME than what we check inside PopAndDestroy(). For some reason, it works so that we're pushing is a CSomething pointer and we're trying to destroy is an MSomeObserver pointer. Don't ask me why it works this way, but it does. And the problem disappears right after declaring base classes in the 'right' order, i.e. having CBase at the first position.
Tote
Re: Some tips on C classes
stichbury
| 04/03/2007, 01:56
Hi Paul
The member variables of a C class also don't get zeroed on creation if a C class doesn't derive from CBase. This seems to be a common error, which also leads to problems if objects of the class are put on the cleanup stack, since they are pushed using the overload which takes TAny* rather than CBase*, meaning that their destructor isn't called by PopAndDestroy() or in the event of a leave.
Re: Some tips on C classes
tote_b5 | 26/02/2007, 11:34
One more thing to add: use the right order of classes that you derive from in your class declaration. For example, one can assume that if your class is derived from CBase, then some M... classes it would be the same as M... classes then CBase. Of course, any C class will do instead of CBase.
Well, it's not the same. A typical use case is that you have to write the implementation of a mixin class (e.g. an observer) - and you have to make it so that your class also derives from CBase. If you change the "recommended" order so that CBase is not at the first place in the order of declaration which class your class derives from, then the following code won't work:
class CSomething : public MSomeObserver, public CBase
{
...
}
CSomething* sg = CSomething::NewLC();
CleanupStack::PopAndDestroy( sg ); // <-- it will panic!!!
The reason why PopAndDestroy() panics in the above line is that the pointer we've pushed on to the cleanup stack is NOT THE SAME than what we check inside PopAndDestroy(). For some reason, it works so that we're pushing is a CSomething pointer and we're trying to destroy is an MSomeObserver pointer. Don't ask me why it works this way, but it does. And the problem disappears right after declaring base classes in the 'right' order, i.e. having CBase at the first position.
Tote