强指针和弱指针

1.1      指针问题

指针是c,c++语言中的一件大杀器,功能强大,但却很容易造成非常难以解决的bug。

我们经常遇到的指针问题有:

 

1. malloc的内存没有free,这样造成了内存泄露

 

2. 已经delete的指针还在使用,这种就是所谓“野指针”,即指针没有指向一个活着的对象。比如:

class A{
public:
    int item;
    A():item(1){};
};
void f(){
    A *p=new A();
    delete p;
    cout<<p->item<<endl;  //程序奔溃
}

 

3. 某些地址是根本不能读的,比如0x20:

void f(){
    p=(A *)0x20;
    cout<<p->item<<endl;  //程序奔溃
}

 

所以为了避免指针出现的各种问题,在java,c#中则干脆完全隐藏了指针。所以java,c#之类语言开发的程序,基本上编译没问题的话,运行crash的情况比较少。那对于c++程序,为了把指针的可怕控制起来,牛人们想到了一种东西叫智能指针。它能够防止我们上面所能想到的那些错误。

 

那怎么去设计呢?

首先,这个类中要有一个指针,就是要保护起来防止它犯错的东东;

其次,这个类要能搞定所有类型的指针,所以它需要是一个模板类;

然后,自动释放内存。我们选择在类析构的时候,同时去析构指针指向的对象。

 

好了,我们可以简要的写出基本模型如下:

 

class SmartPointer{
    SmartPointer():m_ptr(0){}
private:
    T * m_ptr;
    ~SmartPointer()
    {
        if(m_ptr!=0){ delete m_ptr; };
    }
};

 

但这个模型还没有解决“一个对象被多个指针引用”所可能导致的问题。比如:

 

class A{
public:
    int item;
    A():item(1){};
};
void f(){
    A *p0=new A();

    A *p1=p;

    //经过一段逻辑后,p完成了它的任务,被delete掉了

    delete p0;

    //我们发现刚才捎带也干掉了本不想被干掉的p1
    cout<<p1->item<<endl; //程序奔溃

}

 

为了解决这个问题,很自然的想到了引用计数方法,简单的说,把一个对象赋值给一个智能指针,计数+1;delete一个智能指针,计数-1。当计数为0的时候,真正去delete对象本身。安卓中著名的RefBase就是干这个用的,我们看到很多类都继承于它,比如所有的Bn,Bp类。另外,安卓还提供了一个更轻量级的RefBase的实现:LightRefBase。

Android 5.1版本中,代码在RefBase.h (frameworks\rs\cpp\util)

1.2      LightRefBase和强指针

LightRefBase的定义

template <class T>
class LightRefBase
{
public:
    inline LightRefBase() : mCount(0) { }
    inline void incStrong(__attribute__((unused)) const void*id) const {
        __sync_fetch_and_add(&mCount,1);
    }
    inline void decStrong(__attribute__((unused)) const void*id) const {
        if(__sync_fetch_and_sub(&mCount, 1) == 1) {
            deletestatic_cast<const T*>(this);
        }
    }
    typedef LightRefBase<T> basetype;

protected:
    inline ~LightRefBase() { }

private:
    friend class ReferenceMover;
    inline static void moveReferences(void*, void const*,size_t,
            constReferenceConverterBase&) { }

private:
    mutable volatile int32_t mCount;
};

需要引用计数的类继承这个类,引用计数是mCount,当mCount为0时delete子类。看上去很完美,但Android系统中使用的寥寥无几。

再看强指针的代码StrongPointer.h(frameworks\rs\cpp\util) 

template <typename T>
class sp
{
public:
    inline sp() : m_ptr(0) { }
    sp(T* other);
    sp(const sp<T>& other);
    template<typename U> sp(U* other);
    template<typename U> sp(const sp<U>& other);
    ~sp();
    // Assignment
    sp& operator = (T* other);
    sp& operator = (const sp<T>& other);
    template<typename U> sp& operator = (constsp<U>& other);
    template<typename U> sp& operator = (U* other);
    //! Special optimization for use by ProcessState (and nobodyelse).
    void force_set(T* other);
    // Reset
    void clear();
    // Accessors
    inline  T&      operator*() const  { return *m_ptr; }
    inline  T*      operator->() const { return m_ptr;  }
    inline  T*      get()const         { return m_ptr; }
    // Operators
    COMPARE(==)
    COMPARE(!=)
    COMPARE(>)
    COMPARE(<)
    COMPARE(<=)
    COMPARE(>=)
private:
    template<typename Y> friend class sp;
    template<typename Y> friend class wp;
    void set_pointer(T* ptr);
    T* m_ptr;
};

强指针相比之前设计的那个原型,就是多了几个构造函数,多了几个运算符重载。强指针用起来和指针一样,但是不用手动析构。

比如:

在AACEncoder.cpp(frameworks\av\media\libstagefright\codecs\aacenc) 

sp<MediaSource>   mSource;

mSource->read(&mInputBuffer, options)

mSource->stop();

 

1.3      RefBase和弱指针

好了,那么强指针有没有问题呢?我们知道,凡是引用计数相关的逻辑,都需要考虑循环引用怎么办。对于两个强指针对象A和B,A引用了B,B又引用了A,当你去析构A的时候,B的计数减1,然后它试图析构自己,而B析构的时候,需要先去析构A。这样就造成了循环依赖。

为了解决这一问题,我们引入了弱指针的概念,定义两个强指针不能循环引用,一旦循环引用,那么必须一个是强,一个是弱,或者两个都是弱。弱指针也是针对一个对象而言的,一旦这个对象的强指针引用为0,那么无论弱指针是否为0,该对象会被析构。所以,此时一个对象就有了两个计数,强指针计数和弱指针计数。此时LightRefBase就不够用了,因此引入了RefBase。LightRefBase仅提供强指针计数,而RefBase提供了强指针计数和弱指针计数。而weakref_impl就是它的计数器

class RefBase
{
public:
   void           incStrong(const void* id) const;
   void           decStrong(const void* id) const;
   void           forceIncStrong(const void* id) const;
    weakref_type*   createWeak(const void* id) const;
    weakref_type*   getWeakRefs() const;
    typedef RefBase basetype;
protected:
    RefBase();
   virtual                ~RefBase();
    virtualvoid           onFirstRef();
    virtualvoid           onLastStrongRef(const void* id);
    virtualbool           onIncStrongAttempted(uint32_t flags, const void* id);
    virtualvoid           onLastWeakRef(const void* id);
private:
    RefBase(const RefBase& o);
    RefBase&       operator=(const RefBase& o);
    weakref_impl* const mRefs;
};

我们看看weekref_type

    class weakref_type
    {
    public:
       RefBase*           refBase() const;
       void               incWeak(const void* id);
       void               decWeak(const void* id);
        // acquires a strong reference ifthere is already one.
       bool               attemptIncStrong(const void* id);
        // acquires a weak reference ifthere is already one.
        // This is not always safe. seeProcessState.cpp and BpBinder.cpp
        // for proper use.
       bool               attemptIncWeak(const void* id);
       void               trackMe(bool enable, bool retain);
    };

再看看

class RefBase::weakref_impl : public RefBase::weakref_type
{
public:
    volatile int32_t    mStrong;
    volatile int32_t    mWeak;
    RefBase* const      mBase;
    volatile int32_t    mFlags;
    weakref_impl(RefBase* base)
        : mStrong(INITIAL_STRONG_VALUE)
        , mWeak(0)
        , mBase(base)
        , mFlags(0)
    {
    }

    void addStrongRef(const void* /*id*/) { }
    void removeStrongRef(const void* /*id*/) { }
    void renameStrongRefId(const void* /*old_id*/, const void*/*new_id*/) { }
    void addWeakRef(const void* /*id*/) { }
    void removeWeakRef(const void* /*id*/) { }
    void renameWeakRefId(const void* /*old_id*/, const void*/*new_id*/) { }
    void printRefs() const { }
    void trackMe(bool, bool) { }

}

1.3.1       RefBase::incStrong

我们看看RefBase中的incStrong

void RefBase::incStrong(const void* id) const
{
    weakref_impl* const refs = mRefs;
    refs->incWeak(id);
    refs->addStrongRef(id);//这句没有实现
    const int32_t c = android_atomic_inc(&refs->mStrong);
    if (c != INITIAL_STRONG_VALUE)  {
        return;
    }
    android_atomic_add(-INITIAL_STRONG_VALUE,&refs->mStrong);
    refs->mBase->onFirstRef();
}

我们看到,它增加了强指针计数,也增加了弱指针计数,而第一次被引用的时候,会调用onFirstRef

1.3.2       RefBase::weakref_type::incWeak

而看incWeak是仅增加弱计数

void RefBase::weakref_type::incWeak(const void* id)
{
    weakref_impl* const impl = static_cast<weakref_impl*>(this);
    impl->addWeakRef(id);
    const int32_t c __unused =android_atomic_inc(&impl->mWeak);
}

weakref_impl定义在RefBase.cpp (system\core\libutils) 中,就是weakref_type的子类。

1.3.3       RefBase::decStrong

#define INITIAL_STRONG_VALUE (1<<28)

在weakref_impl中,mStrong被初始化为INITIAL_STRONG_VALUE,即

 

看核心的减少引用的代码

 

void RefBase::decStrong(const void* id) const
{
    weakref_impl* const refs = mRefs;
    refs->removeStrongRef(id);
    const int32_t c = android_atomic_dec(&refs->mStrong);
    if (c == 1) {
       refs->mBase->onLastStrongRef(id);
        if((refs->mFlags&OBJECT_LIFETIME_MASK) == OBJECT_LIFETIME_STRONG) {
            delete this;
        }
    }
    refs->decWeak(id);
}

我们看到,不管弱引用,只需要强引用为0时,对象就被销毁了。

1.3.4       RefBase::weakref_type::decWeak

void RefBase::weakref_type::decWeak(const void* id)
{
    weakref_impl* const impl =static_cast<weakref_impl*>(this);
    impl->removeWeakRef(id);
    const int32_t c = android_atomic_dec(&impl->mWeak);
    ALOG_ASSERT(c >= 1, "decWeak called on %p too manytimes", this);
    if (c != 1) return;

    if ((impl->mFlags&OBJECT_LIFETIME_WEAK) ==OBJECT_LIFETIME_STRONG) {
        // This is the regular lifetimecase. The object is destroyed
        // when the last strong referencegoes away. Since weakref_impl
        // outlive the object, it is notdestroyed in the dtor, and
        // we'll have to do it here.
        if (impl->mStrong ==INITIAL_STRONG_VALUE) {
            // Specialcase: we never had a strong reference, so we need to
            // destroythe object now.
            delete impl->mBase;
        } else {
            //ALOGV("Freeing refs %p of old RefBase %p\n", this, impl->mBase);
            delete impl;
        }
    } else {
        // less common case: lifetime isOBJECT_LIFETIME_{WEAK|FOREVER}
        impl->mBase->onLastWeakRef(id);
        if((impl->mFlags&OBJECT_LIFETIME_MASK) == OBJECT_LIFETIME_WEAK) {
            // this isthe OBJECT_LIFETIME_WEAK case. The last weak-reference
            // is gone,we can destroy the object.
            deleteimpl->mBase;
        }
    }
}

由于弱引用肯定会大于等于强引用,所以当弱引用为0时,强应用计数要么为INITIAL_STRONG_VALUE,要么为0。第一种情况下,使用弱引用来delete对象,第二种情况下,使用强引用来delete对象。

1.3.5         弱指针


弱指针定义在RefBase.h(frameworks\rs\cpp\util)

template <typename T>
class wp
{
public:
    typedef typename RefBase::weakref_type weakref_type;

    inline wp() : m_ptr(0) { }

    wp(T* other);
    wp(const wp<T>& other);
    wp(const sp<T>& other);
    template<typename U> wp(U* other);
    template<typename U> wp(const sp<U>& other);
    template<typename U> wp(const wp<U>& other);

    ~wp();

    // Assignment

    wp& operator = (T* other);
    wp& operator = (const wp<T>& other);
    wp& operator = (const sp<T>& other);

    template<typename U> wp& operator = (U* other);
    template<typename U> wp& operator = (constwp<U>& other);
    template<typename U> wp& operator = (constsp<U>& other);

    void set_object_and_refs(T* other, weakref_type* refs);

    // promotion to sp

    sp<T> promote() const;

    // Reset

    void clear();

    // Accessors

    inline  weakref_type* get_refs() const { return m_refs;}

    inline  T* unsafe_get() const { return m_ptr; }

    // Operators


private:
   T*             m_ptr;
    weakref_type*   m_refs;
};

它靠weakref_type类型的对象m_refs来计算引用计数

而m_refs靠createWeak进行赋值:

template<typename T>
wp<T>::wp(T* other)
    : m_ptr(other)
{
    if (other) m_refs = other->createWeak(this);
}

而createWeak返回的就是对象的mRefs

RefBase::weakref_type* RefBase::createWeak(const void* id) const
{
    mRefs->incWeak(id);
    return mRefs;
}

 

 

弱指针要想被使用,首先需要转成强指针,此时需要调用

sp<T> promote() const;

它实现为

template<typename T>
sp<T> wp<T>::promote() const
{
    sp<T> result;
    if (m_ptr &&m_refs->attemptIncStrong(&result)) {
        result.set_pointer(m_ptr);
    }
    return result;
}

我们可以看到,当弱指针被析构后,m_ptr 为null,因此返回null

注意,弱指针中有一项为m_refs,它本质上是一个weakref_type对象

注意,弱指针没有重载->,*等运算符

 

在使用弱指针的时候,由于可能此时已经被析构,需要先检查一下是否被析构,这个通过弱指针升级为强指针来做。弱指针也指向一个对象,但是弱指针仅仅记录该对象的地址,不能通过弱指针来访问该对象,也就是说不能通过弱智真来调用对象的成员函数或访问对象的成员变量。要想访问弱指针所指向的对象,需首先将弱指针升级为强指针(通过wp类所提供的promote()方法)。弱指针所指向的对象是有可能在其它地方被销毁的,如果对象已经被销毁,wp的promote()方法将返回空指针,这样就能避免出现地址访问错的情况。

 

网上不错的文章 http://m.blog.csdn.net/blog/qq295445028/37520185

给个类关系图:



弱指针例子:

AudioPolicyService.h (frameworks\av\services\audiopolicy)

wp<AudioPolicyService> mService;

svc = mService.promote();
                     if(svc == 0) {
                       break;
                   }
                   mLock.unlock();
                   svc->doStopOutput(data->mIO, data->mStream, data->mSession);

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值