您的位置:首页 > 其它

智能指针

2017-10-09 21:24 176 查看

智能指针

智能指针
简介

强指针sp

弱指针wp

重要函数实现

总结



1.简介

1.指针问题的常见来源

指针没有初始化

new了对象后没有及时delete,导致内存泄漏。

野指针;使用delete后的对象,会导致系统崩溃。

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

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.总结

智能指针分为强指针sp和弱指针wp两种;

通常情况下,目标对象的父类都是RefBase——这个基类提供一个weakref_impl类型的引用计数器,可以同时进行强弱引用的控制(内部由mStrong和mWeak提供计数);

当incStrong增加强引用计数时,也会增加弱引用计数;

当incWeak时只增加弱引用计数;

使用者也可以通过extendObjectLifetime设置引用计数器的规则,不同规则下对删除目标对象的时机判断也是不一样的。

使用者可以根据程序需求来选择合适的智能指针类型和计数器规则。

参考链接:

理解Refbase强弱引用

Android系统的智能指针(轻量级指针、强指针和弱指针)的实现原理分析
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  智能指针