关闭

Symbian 智能指针

613人阅读 评论(1) 收藏 举报

Symbian 智能指针
在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<HBufC> wbuf(HBufC::NewL(10));    //永远不要调用NewLC, CPtr内部会PushL的
   CPtr<HBufC> wbuf2(HBufC::NewL(20));   //同上

   RPtr<RFs> 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<CFoo> 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<HBufC> 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 <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_(a)
    {
        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
 

0
0

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:780062次
    • 积分:14370
    • 等级:
    • 排名:第900名
    • 原创:421篇
    • 转载:340篇
    • 译文:1篇
    • 评论:64条
    文章分类
    最新评论