智能指针

本文介绍了智能指针的概念,包括指针问题的来源及如何设计智能指针。详细讲解了强指针sp和弱指针wp的实现原理,涉及引用计数、生命周期管理,并探讨了它们在解决循环引用问题中的作用。同时,讨论了RefBase类及其在智能指针管理中的重要角色。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

智能指针

总体描述

1.简介

1.指针问题的常见来源

  • 指针没有初始化
  • new了对象后没有及时delete,导致内存泄漏。
  • 野指针;使用delete后的对象,会导致系统崩溃。

通过delete释放了对象,但没有将指针置空。

2.如何设计一个智能指针

考虑的因素:

  1. 初始化;
  2. 实现new和delete的配套;

具体的设计实现:

我们将智能指针称为SmartPointer。

  • SmartPointer是一个类

首先想到的是,SmartPointer要能记录内存对象Object的地址,它的内部应该有一个变量指向Object,所以SmartPointer是一个类。

class SmartPointer{
    private:
        void* m_ptr;//指向Object对象
}
  • SmartPointer是一个模板类

智能指针并不是针对某种特定类型的对象而设计的,因而一定是模板类。

template <typename T> class SmartPointer{
    private:
        T* m_ptr;//指向Object对象
}
  • SmartPointer的构造函数

智能指针需要考虑的一个问题就是初始化,所以智能指针的构造函数应将m_ptr置空。

template <typename T> class SmartPointer{
    inline SmartPointer() : m_ptr(0) {}
    private:
        T *m_ptr;//指向Object
}
  • 引用计数

智能指针还需考虑的一个问题是什么时候释放一个内存对象?可以考虑通过引用计数来统计对象使用的次数,如果对象的引用次数为0了,则可以释放该对象了。引用计数该由谁来管理呢?

如果由智能指针自己来维护引用计数的话,将会出现引用计数不一致的情况,导致致命错误。

如果由引用对象Object自己维护计数器,则可以解决此问题。我们可以定义一个统一的具备计数功能的父类Object。

template <class T> class LightRefBase{
    public:
        inline LightRefBase() : mCount(0) {}
        // 增加引用计数
        inline void incStrong() const {
            android_atomic_inc(&mCount);
        }
        // 减小引用计数
        inline void decStrong() const {
            if(android_atomic_dec(&mCount) == 1){
                delete static_cast<const T*>(this);//删除内存对象
            }
        }
    protected:
        inline ~LightBaseRef() {}
    private:
        muteable volatile int32_t mCount;//引用计数值
}

为此智能指针SmartPointer需要重载“=”运算符,其定义也需要修改:

template <typename T> class SmartPointer{
    inline SmartPointer() : m_ptr(0) {}
    ~SmartPointer();
    SmartPointer& operator = (T* other);//重载运算符

    private:
        T* m_ptr;
}

template <typename T> SmartPointer<T>& SmartPointer<T>::operator = (T *other){
    if(other != null){
        other->incStrong();//主动增加计数值
    }
    if(m_ptr){
        m_ptr->decStrong();// 避免重复赋值的情况
    }
    m_ptr = other;//指向内存对象Object
    return *this;
}

template <typename T> SmartPointer<T>::~SmartPointer(){
    if(m_ptr){
        m_ptr->decStrong();
    }
}

普通引用计数将可能出现循环引用的情况,为此需要采用另外一种引用计数技术,即对象的引用计数同时存在强引用计数和弱引用计数两种。它们遵循这样的规则:

  • 只要对象的强引用计数为0,不管它的弱引用计数是否为0,都可以回收该对象;
  • 弱指针必须先升级为强指针,才能访问它所指向的目标对象;如果使用一个只持有弱引用计数的对象,则需要先把这个弱引用对象升级为强引用,才能使用该对象。如果升级失败,则说明该对象已经不存在了,不能再使用了。

3.智能指针分类

根据引用计数器类以及智能指针对象类,可以将智能指针分为三大类:

  • 轻量级指针(Light Pointer)
    • 引用计数类为LightRefBase类;
    • 智能指针对象类为sp类;
  • 强指针(Strong Pointer)
    • 引用计数类为RefBase类;
    • 智能指针对象类为sp类;
  • 弱指针(Weak Pointer)
    • 引用计数类为RefBase类;
    • 智能指针对象类为wp类;

实现智能指针的过程,概括来说,可以归结为两步:

  • 第一是定义一个负责提供引用计数的类;
  • 第二是实现相应的智能指针对象类,用来管理引用计数;

2.强指针sp

sp是StrongPointer的简写,StrongPointer定义在frameworks/native/include/utils/StrongPointer.h

sp类的定义如下:

template <typename T>
class sp
{
public:
inline sp() : m_ptr(0) { }

sp(T* other);
sp(const sp<T>& other);
.......其他构造函数
~sp();// 析构函数


//重载运算符
sp& operator = (T* other);
sp& operator = (const sp<T>& other);
.....
void force_set(T* other);

void clear();
// 重载访问符
inline  T&      operator* () const  { return *m_ptr; }
inline  T*      operator-> () const { return m_ptr;  }
inline  T*      get() const         { return m_ptr; }

private:
template<typename Y> friend class sp;
template<typename Y> friend class wp;
void set_pointer(T* ptr);
T* m_ptr;
};

template<typename T>
sp<T>::sp(T* other)
: m_ptr(other)
{
    if (other) other->incStrong(this);// 增加引用计数
}

template<typename T>
sp<T>& sp<T>::operator = (T* other)
{
    if (other) other->incStrong(this); 
    if (m_ptr) m_ptr->decStrong(this);
    m_ptr = other;
    return *this;
}

template<typename T>
void sp<T>::force_set(T* other)
{
    other->forceIncStrong(this);
    m_ptr = other;
}

template<typename T>
void sp<T>::clear()
{
    if (m_ptr) {
        m_ptr->decStrong(this);
        m_ptr = 0;
}
}

template<typename T>
void sp<T>::set_pointer(T* ptr) {
    m_ptr = ptr;
}

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) {
            // 删除内存对象
            delete static_cast<const T*>(this);
        }
    }

typedef LightRefBase<T> basetype;

protected:
    inline ~LightRefBase() { }

private:
mutable volatile int32_t mCount;//引用计数值
};

3.弱指针wp

wp是WeakPointer的简写,弱指针的出现是为了解决循环引用的问题。

双方规定:

当强引用计数为0时,不论弱引用是否为0都可以delete自己。为了避免野指针的出现,还有一个规定:弱指针必须先升级为强指针,才能访问它所指向的目标对象。

弱指针的主要使命就是解决循环引用的问题。

与sp相比,wp在定义上有如下重要的区别:

  • 除了指向目标对象的m_ptr外,wp另外有一个m_refs指针,类型为weakref_type;
  • 没有重载->,*等运算符。
  • 有一个promote方法来将wp提升为sp
  • 目标对象的父类不是LightRefBase,而是RefBase。

wp类的定义如下:

template <typename T>
class wp
{
public:
    typedef typename RefBase::weakref_type weakref_type;
        inline wp() : m_ptr(nullptr) { }

    wp(T* other);//构造函数

    ~wp();//析构函数

    wp& operator = (T* other);// 运算符重载

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

    sp<T> promote() const;// 升级为强指针

    void clear();

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

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

private:
    template<typename Y> friend class sp;
    template<typename Y> friend class wp;

    T*              m_ptr;
    weakref_type*   m_refs;
};

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

在sp的构造函数中,调用的incStrong方法增加引用计数,而wp没有直接增加目标对象的引用计数值,而是调用了createWeak方法。该方法定义在RefBase类中。

RefBase类的定义如下:

class RefBase
{
    public:
        void            incStrong(const void* id) const;// 增加强引用计数
        void            decStrong(const void* id) const;// 减小强引用计数
// 嵌套内部类,wp中用到的就是这个类
class weakref_type
{
public:
    RefBase*            refBase() const;

    void                incWeak(const void* id);//增加弱引用计数
    void                decWeak(const void* id);// 减小弱引用计数

    bool                attemptIncStrong(const void* id);
    bool                attemptIncWeak(const void* id);
};

weakref_type*   createWeak(const void* id) const;// 生成一个weakref_type类型

weakref_type*   getWeakRefs() const;

typedef RefBase basetype;

protected:
                        RefBase();// 构造函数
virtual                 ~RefBase();// 析构函数

// 以下参数用于修改Object的生命周期
enum {
    OBJECT_LIFETIME_STRONG  = 0x0000,
    OBJECT_LIFETIME_WEAK    = 0x0001,
    OBJECT_LIFETIME_MASK    = 0x0001
};

void  extendObjectLifetime(int32_t mode);


enum {
    FIRST_INC_STRONG = 0x0001
};

virtual void            onFirstRef();
virtual void            onLastStrongRef(const void* id);
virtual bool            onIncStrongAttempted(uint32_t flags, const void* id);
virtual void            onLastWeakRef(const void* id);

private:
    friend class weakref_type;
    class weakref_impl;

    RefBase(const RefBase& o);
    RefBase&    operator=(const RefBase& o);
    weakref_impl* const mRefs;
};

RefBase嵌套了一个重要的类weakref_type,也就是wp中的m_refs所属的类型。在RefBase中还有一个mRefs的成员变量,类型为weakref_type_impl,该类型是weakref_type的实现类。

weakref_type_impl的类型如下:

#define INITIAL_STRONG_VALUE (1<<28)
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 addWeakRef(const void* /*id*/) { }
void removeWeakRef(const void* /*id*/) { }
}

wp类与RefBase、weakref_type以及weakref_type_impl之间的关系如下:

智能指针

4.重要函数实现

接下来看几个比较重要方法的实现:

1.RefBase:createWeak

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

该函数首先增加了mRefs中的弱引用计数值,然后返回这个mRefs。mRefs是一个weakref_type类型,具体的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);
ALOG_ASSERT(c >= 0, "incWeak called on %p after last weak ref", this);
}

weakref_impl中的mWeak是一个整型变量,记录的是弱引用计数。

可以看到,RefBase和LightRefBase不同,LightRefBase是直接采用int变量来保存引用计数值,而RefBase是采用了weakref_type类型的计数器。这是因为RefBase需要处理多种计数类型。wp和RefBase都指向了weakref_type类型的计数器。

wp与sp相比,只是采用了一种新的计数器weakref_impl而已,其他的工作都是围绕如何操作这个计数器而展开的。

2.RefBase::incStrong

当wp构造完成后,RefBase所持有的weakref_type计数器的mWeak值就为1,后面如果有新的wp指向这个对象,mWeak还会持续增加。如果是sp呢?

sp会调用目标对象的incStrong方法来增加强引用计数值,当目标对象继承自RefBase时,这个函数的实现就是:

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);//增加强引用计数
ALOG_ASSERT(c > 0, "incStrong() called on %p after last strong ref", refs);
// 判断是不是第一次
if (c != INITIAL_STRONG_VALUE)  {
    return;// 不是第一次,则直接返回
}
// 给mStrong赋初始值为1 
android_atomic_add(-INITIAL_STRONG_VALUE, &refs->mStrong);
// 回调onFirstRef通知对象自己被引用了
refs->mBase->onFirstRef();
}

该方法的主要功能:

  • 分别增加weakref_impl的强弱引用计数(mStrong/mWeak),进行加1操作;
  • 当首次调用incStrong,则再回调目标对象的onFirstRef()方法,比如ProcessState对象。

接下来看看目标对象在什么情况下会被释放,无非就是考察减少强弱引用时系统所遵循的规则。

3.RefBase::decStrong

void RefBase::decStrong(const void* id) const
{
weakref_impl* const refs = mRefs;
refs->removeStrongRef(id);// 调试目的
const int32_t c = android_atomic_dec(&refs->mStrong);// 减少引用计数
ALOG_ASSERT(c >= 1, "decStrong() called on %p too many times", refs);
// 减小后强引用计数值已经为0了
if (c == 1) {
    refs->mBase->onLastStrongRef(id);//通知事件
    // 删除对象
    if ((refs->mFlags&OBJECT_LIFETIME_MASK) == OBJECT_LIFETIME_STRONG) {
        delete this;
    }
}
refs->decWeak(id); // 减少弱引用计数
}

在该方法中,首先减少mStrong的引用计数,如果发现已经减到0(即c==1),就需要回调onLastStrongRef方法通知该事件,接着执行删除操作(如果标志位是OBJECT_LIFETIME_STRONG)。
需要注意的是在减小强引用计数值的同时,还有减小弱引用计数值。即最后调用的decWeak()方法。

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 many times", this);
如果发现还不为0,即c != 1,就直接返回;否则弱引用的计数值也为0,此时需要根据LIFETIME标志分别处理。
if (c != 1) return;

// 当标志为OBJECT_LIFETIME_STRONG时,释放规则受强引用控制
if ((impl->mFlags&OBJECT_LIFETIME_WEAK) == OBJECT_LIFETIME_STRONG) {
    // 强引用值为INITIAL_STRONG_VALUE,说明该目标对象没有被强引用过,也就是说该目标对象无法靠强指针来释放目标对象,所以释放实际目标对象
    if (impl->mStrong == INITIAL_STRONG_VALUE) {
        delete impl->mBase;
    } else {
        // 在有强引用的情况下,此时要释放weakref_impl对象,而目标对象会由强引用的decStrong来释放
        delete impl;
    }
} else {
    impl->mBase->onLastWeakRef(id);// 通知事件
    if ((impl->mFlags&OBJECT_LIFETIME_MASK) == OBJECT_LIFETIME_WEAK) {
        delete impl->mBase;
    }
}
}

所引用主要是基于以下标志来处理的:

enum {
    OBJECT_LIFETIME_STRONG  = 0x0000,// 强引用生命周期
    OBJECT_LIFETIME_WEAK    = 0x0001,// 弱引用生命周期
    OBJECT_LIFETIME_MASK    = 0x0001
};

每个目标对象都可以通过以下方法来更改它的引用规则:

void RefBase::extendObjectLifetime(int32_t mode)
{
    android_atomic_or(mode, &mRefs->mFlags);
}

生命周期

  • flags为OBJECT_LIFETIME_STRONG时,强引用计数控制实际对象的生命周期,弱引用计数控制weakref_impl对象的生命周期。

    • 强引用计数为0时,实际对象被delete。对于这种情况,应使用wp时由弱生强promote()。
  • flags为OBJECT_LIFETIME_WEAK时,强引用计数为0,弱引用计数不为0时,实际对象不会被delete。

    • 当弱引用计数减为0时,实际对象和weakref_impl对象会同时被delete。
  • flags为LIFETIME_FOREVER,对象不受强弱引用计数的控制,永不会被回收。

5.RefBase::RefBase和RefBase::~RefBase

最后来看下RefBase的构造函数和析构函数:

构造函数

RefBase::RefBase()
: mRefs(new weakref_impl(this))
{
}

析构函数

RefBase::~RefBase()
{

if (mRefs->mStrong == INITIAL_STRONG_VALUE) {
    delete mRefs;
} else {
    if ((mRefs->mFlags & OBJECT_LIFETIME_MASK) != OBJECT_LIFETIME_STRONG) {
        // 弱引用计数为0
        if (mRefs->mWeak == 0) {
            // 释放weakref_impl对象
            delete mRefs;
        }
    }
}
const_cast<weakref_impl*&>(mRefs) = NULL;
}

6.wp::promote()

template<typename T>
sp<T> wp<T>::promote() const
{
    sp<T> result;
    // m_ptr不为空,且增加强引用计数成功
    if (m_ptr && m_refs->attemptIncStrong(&result)) {
        result.set_pointer(m_ptr);
    }
    return result;
}

// 设置强引用指针
template<typename T>
void sp<T>::set_pointer(T* ptr) {
    m_ptr = ptr;
}

该方法是将弱指针转换为强指针。

5.总结

  1. 智能指针分为强指针sp和弱指针wp两种;
  2. 通常情况下,目标对象的父类都是RefBase——这个基类提供一个weakref_impl类型的引用计数器,可以同时进行强弱引用的控制(内部由mStrong和mWeak提供计数);
  3. 当incStrong增加强引用计数时,也会增加弱引用计数;
  4. 当incWeak时只增加弱引用计数;
  5. 使用者也可以通过extendObjectLifetime设置引用计数器的规则,不同规则下对删除目标对象的时机判断也是不一样的。
  6. 使用者可以根据程序需求来选择合适的智能指针类型和计数器规则。

参考链接:

理解Refbase强弱引用

Android系统的智能指针(轻量级指针、强指针和弱指针)的实现原理分析

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值