本文共 5772 字,大约阅读时间需要 19 分钟。
在Symbian开发过程中, 由于没有确定性析构, 最让人烦躁的就是Cleanup Stack的操作, 当在一个函数中使用局部变量时, 要记得PushL, 然后在函数末尾, 还要PopAndDestroy, 变量一多, 头昏脑胀,都找不到北了, 经常被Panic, 而且在VC6窗口中还不知道到底是哪行的问题, 只能单步调试, 一直到程序crash. 然后知道上一行就是问题所在.下面是我写的一个智能指针类( 实际上是两个, 一个CPtr用于C class, 一个RPtr 用于R class), 我自己的使用经验表明可以免除90%的对Cleanup Stack的操作. (实际上我在新代码中从来不再手动使用CleanupStack了)例如一个简单的代码:void FunL(){ HBufC * wbuf = HBufC::NewL(10); CleanupStack::PushL(wbuf); //有点造作, 应该直接的用HBufC::NewLC, 仅仅是举个例子, 因为很多类只提供了NewL HBufC * wbuf2 = HBufC::NewL(20); CleanupStack::PushL(wbuf2); RFs fs; User::LeaveIfEror( fs.Connect()); CleanupClosePushL(fs); do_sth_maybe_leaveLLL(); CleanupStack::PopAndDestroy(3); //关文件fs, 删除wbuf, wbuf2}如果函数中又增加一个C对象的使用, 那么要手动PushL一次, 然后在结尾还要记得将PopAndDestroy(3) 改为 4. 如果利用智能指针, 上面的代码可以简化为:void FunL(){ using namespace sbl; //封装在sbl中 CPtr wbuf(HBufC::NewL(10)); //永远不要调用NewLC, CPtr内部会PushL的 CPtr wbuf2(HBufC::NewL(20)); //同上 RPtr fs; User::LeaveIfEror( fs->Connect()); //fs.Connect() 改为 fs->Connect() do_sth_maybe_leaveLLL(); }以后就什么都不用管了, Leave也好, 不Leave也好, wbuf, wbuf2最终都会被delete, fs都会被关闭.记住的是:1. 不要对成员变量使用CPtr, RPtr (你应该知道, 成员变量本身就不应该PushL)2. 在一个函数中不要混合使用CleanupStack 和 智能指针类. 要不你就手动的PushL, 要不就全部交给智能指针. 3. 在给CPtr智能指针初始化/赋值的时候, 永远不要调用Cxxx::NewLC, (因为NewLC自己PushL一次了), 而总是调用NewL.另外, 有了智能指针, 基本上你无需再给你的class提供NewLC了, 而且NewL中的实现可以如下:/* static */CFoo * CFoo::NewL(){ CPtr self( new (ELeave)CFoo); //放心, CPtr内部已经PushL了, 保护了这个self self->ConstructL(); //Leave也无问题, 会调用delete self的. return self.release(); //release 释放所有权, 这样CPtr析构的时候不会再去delete self.}基本上, 你了解STL中的auto_ptr, 也就了解了CPtr. RPtr有点不同, 主要用于使用R class, 它内部放的不是T*,而是直接T本身.再举个CPtr的例子:void foo(){ CPtr wbuf(HBufC::NewL(20)); //分配内存 *wbuf = _L("Hello,world"); //给 HBufC 赋值 wbuf = 0; //释放, 注意HBufC已经释放了, 或者 reset(0)也可以 wbuf = HBufC::NewL(20); //又分配内存 wbuf = HBufC::NewL(40); //哎呀, 不够, 释放刚刚分配的, 分配一块大的内存 *wbuf = _L("long long long hello, world ");}基本上和stl中的auto_ptr使用没有什么分别. 不过由于symbian的cleanup机制, 不能将CPtr/RPtr作为成员变量.下面是源代码: (使用的时候别忘了CPtr, RPtr都是在sbl namespace中, 另外, debug版本中用到了c library的assert)#include #include #include 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 studytemplateclass 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& other) { ptr_ = other.release(); CleanupPushL(); } //assignment, take ownership, just like std::auto_ptr CPtr& operator=(CPtr& other) { if(this != &other) { assert( ptr_ != other.ptr_); reset(other.release()); } return *this; } CPtr& operator=(T* ptr) { reset(ptr); return *this; } /* sorry, due to buggy vc6 template CPtr(CPtr& other) { CleanupPushL(); ptr_ = other.release(); } template CPtr& operator=(CPtr& 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 pointertemplatevoid CPtr::OnLeave(void * p){ CPtr * cptr = static_cast(p); cptr->reset(0);}//default R class uses Close() to release resourcetemplateclass RTrait{ public: static void Dispose(R& r) { r.Close(); } static bool Connected(const R& r);};// default R class check binary bits to determine if connectedtemplatebool RTrait::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 RPtr{ public: RPtr() { assert(!Trait::Connected(res_)); CleanupStack::PushL(TCleanupItem( OnLeave, this)); } template RPtr(const A1& a) : res_(a) { CleanupStack::PushL(TCleanupItem( OnLeave, this)); } /* template RPtr(const A1& a1, const A2& a2) : res_(a1, a2) { CleanupStack::PushL(TCleanupItem( DisposeInvoker, this)); } template 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&);};templatevoid RPtr::OnLeave(void * p){ RPtr* self = static_cast< RPtr*>(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 来自 “ ITPUB博客 ” ,链接:http://blog.itpub.net/10294527/viewspace-126429/,如需转载,请注明出处,否则将追究法律责任。
转载于:http://blog.itpub.net/10294527/viewspace-126429/