In the Symbian development process, because there is no deterministic structure, the most annoying thing is the cleanup stack operation. When using a local variable in a function,
Remember pushl, and then at the end of the function, you still need popanddestroy. There are a lot of variables, and you cannot find the north, often panic, and in the vc6 window
You can only perform one-step debugging until the program crash. Then you know that the last line is the problem.
The following is a smart pointer class I wrote (actually two, one cptr for C class, and one rptr for R class ), my own experience shows that 90% of cleanup stack operations can be exempted. (In fact, I have never used cleanupstack manually in the new Code)
For example, a simple code:
Void funl ()
{
Hbufc * wbuf = hbufc: newl (10 );
Cleanupstack: pushl (wbuf); // It is a bit of creation. hbufc: newlc should be used directly, just for example, because many classes only provide newl
Hbufc * wbuf2 = hbufc: newl (20 );
Cleanupstack: pushl (wbuf2 );
Rfs fs;
User: leaveiferor (FS. Connect ());
Cleanupclosepushl (FS );
Do_sth_maybe_leavelll ();
Cleanupstack: popanddestroy (3); // close the file FS, delete wbuf, wbuf2
}
If a new C object is added to the function, manually pushl it once, and remember to change popanddestroy (3) to 4 at the end.
The above code can be simplified:
Void funl ()
{
Using namespace SBL; // encapsulated in SBL
Cptr Cptr
Rptr <RFS> FS;
User: leaveiferor (FS-> connect (); // change fs. Connect () to FS-> connect ()
Do_sth_maybe_leavelll ();
}
You don't need to worry about anything in the future. Leave, leave, wbuf, and wbuf2 will eventually be deleted, and FS will be closed.
Remember:
1. Do not use cptr or rptr for member variables (you should know that member variables should not pushl themselves)
2. Do not mix cleanupstack and smart pointer classes in a function. Do not manually use pushl or hand it all over to smart pointers.
3. When initializing/assigning values to the cptr smart pointer, always call newl instead of calling CXXX: newlc (because newlc has pushl once.
In addition, with smart pointers, you basically don't need to provide newlc for your class, and the implementation in newl can be as follows:
/* Static */
Cfoo * cfoo: newl ()
{
Cptr <cfoo> Self (New (eleave) cfoo); // rest assured that cptr has pushl internally, protecting this self
Self-> constructl (); // No problem with leave. delete self is called.
Return self. Release (); // release releases the ownership so that the cptr structure will not delete self again.
}
Basically, you know auto_ptr in STL, and cptr. rptr is a little different. It is mainly used to use R class. Instead of T *, it puts T itself directly.
Another example of cptr is as follows:
Void Foo ()
{
Cptr * Wbuf = _ L ("Hello, world"); // assign a value to hbufc
Wbuf = 0; // release. Note that hbufc has been released, or reset (0) can be used.
Wbuf = hbufc: newl (20); // Memory Allocation
Wbuf = hbufc: newl (40); // oh, not enough. Release the allocated one. allocate a large memory.
* Wbuf = _ L ("long Hello, world ");
}
Basically, there is no difference between auto_ptr and STL. However, due to the cleanup mechanism of Symbian, cptr/rptr cannot be used as a member variable.
The following is the source code: (when using cptr, rptr is in SBL namespace, and assert of C library is used in debug)
# Include <e32base. h>
# Include <libc/assert. h>
# Include <libc/string. h>
Namespace SBL // SBL stands for Symbian Library
{
// A auto_ptr for any object can be free by "delete" Operator
// If you know STD: auto_ptr then no much thing you need to study
Template <class T>
Class cptr
{
Public:
// Take a raw pointer, this pointer must not been pushed into cleanup Stack
Explicit cptr (T * PTR = 0): PTR _ (PTR)
{
// No matter how we need a slot in cleanup Stack
Cleanuppushl ();
}
// Copy ctor, take ownership, just like STD: auto_ptr
Cptr (cptr <t> & other)
{
PTR _ = Other. Release ();
Cleanuppushl ();
}
// Assignment, take ownership, just like STD: auto_ptr
Cptr <t> & operator = (cptr <t> & other)
{
If (this! = & Other ){
Assert (PTR _! = Other. PTR _);
Reset (other. Release ());
}
Return * this;
}
Cptr <t> & operator = (T * PTR)
{
Reset (PTR );
Return * this;
}
/* Sorry, due to buggy vc6
Template <Class U>
Cptr (cptr <u> & other)
{
Cleanuppushl ();
PTR _ = Other. Release ();
}
Template <Class U>
Cptr <t> & operator = (cptr <u> & other)
{
Reset (other. Release ());
Reutrn * this;
}
*/
T & operator * () const
{
Assert (PTR _! = 0 );
Return * PTR _;
}
T * operator-> () const
{
Assert (PTR _! = 0 );
Return PTR _;
}
// Get the raw pointer explicitly
T * Get () const
{
Return PTR _;
}
Void reset (T * PTR = 0)
{
If (PTR! = PTR _)
{
Delete PTR _; // here we use "delete" to free resource
PTR _ = PTR;
}
}
// Release ownership
T * release ()
{
T * TMP = PTR _;
PTR _ = 0;
Return TMP;
}
// Normally exit, dispose
~ Cptr ()
{
Cleanupstack: popanddestroy (1, this); // remove from cleanup Stack
}
Typedef void (* safe_bool) (void * P );
// Used by if (c)
Operator safe_bool () const
{
Return PTR _? & Dispose: 0;
}
// Used by if (! C)
Bool Operator! () Const
{
Return safe_bool (* This) = 0;
}
PRIVATE:
T * PTR _;
Void cleanuppushl ()
{
Cleanupstack: pushl (tcleanupitem (onleave, this ));
}
Static void onleave (void * P );
};
// This function isn' t inline since cleanup stack want our function pointer
Template <class T>
Void cptr <t>: onleave (void * P)
{
Cptr * cptr = static_cast <cptr *> (P );
Cptr-> Reset (0 );
}
// Default R class uses close () to release resource
Template <class r>
Class rtrait
{
Public:
Static void dispose (R & R)
{
R. Close ();
}
Static bool connected (const R & R );
};
// Default R class check binary bits to determine if connected
Template <class r>
Bool rtrait <r >:: connected (const R & R)
{
Const char * Start = (const char *) & R;
Const char * end = start + sizeof (R );
For (; start! = End; Start ++)
{
If (* start! = 0)
Return true;
}
Return false;
}
Template <class R, class trait = rtrait <r>
Class rptr
{
Public:
Rptr ()
{
Assert (! Trait: connected (RES _));
Cleanupstack: pushl (tcleanupitem (onleave, this ));
}
Template <Class A1>
Rptr (const A1 & A): RES _ ()
{
Cleanupstack: pushl (tcleanupitem (onleave, this ));
}
/*
Template <Class A1, class A2>
Rptr (const A1 & A1, const A2 & A2): RES _ (a1, a2)
{
Cleanupstack: pushl (tcleanupitem (disposeinvoker, this ));
}
Template <Class A1, class A2, class A3>
Rptr (const A1 & A1, const A2 & A2, const A3 & A3): RES _ (A1, A2, A3)
{
Cleanupstack: pushl (tcleanupitem (disposeinvoker, this ));
}
*/
~ Rptr ()
{
Cleanupstack: popanddestroy (1, this); // remove from cleanup stack and delete
}
R * operator-> ()
{
Return & RES _;
}
R & operator *()
{
Return RES _;
}
R & get ()
{
Return RES _;
}
Operator R &()
{
Assert (safe_bool (* This ));
Return RES _;
}
Typedef void (* safe_bool) (void *);
// Used by if (r)
Operator safe_bool () const
{
Return trait: connected (RES _)? & Onleave: 0;
}
// Used by if (! R)
Bool Operator! () Const
{
Return safe_bool (* This) = 0;
}
PRIVATE:
R RES _;
Static void onleave (void * P );
// Noncopyable and assignable
Rptr & operator = (const rptr &);
Rptr (const rptr &);
};
Template <class R, class trait>
Void rptr <R, trait>: onleave (void * P)
{
Rptr <R, trait> * Self = static_cast <rptr <R, trait> *> (P );
// If (* Self) to check if the r class is connected
If (* Self ){
Trait: dispose (self-> RES _);
Assert (! Trait: connected (self-> RES _));
}
}
} // End of namespace
# Endif