智能指针
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.hsp类的定义如下:
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系统的智能指针(轻量级指针、强指针和弱指针)的实现原理分析
相关文章推荐
- Effective c++--智能指针 & 函数模板
- Android系统篇之----Android中的智能指针
- auto_ptr智能指针
- C++智能指针
- c++智能指针的创建
- WebKit的智能指针分析 - Part 1:RefCounted
- 智能指针
- *运算符,->运算符重载与智能指针
- Linux内核里的“智能指针” (续)
- 智能指针引用计数问题
- C++中智能指针的实现原理
- 智能指针 weak_ptr
- C++中智能指针的设计和使用
- 【C++11新特性】 C++11智能指针之unique_ptr
- 智能指针一定智能吗?
- boost智能指针的性能测试
- 智能指针(转)
- C++中智能指针的设计和使用
- 浅谈RAII&智能指针
- 智能指针auto_ptr管理单例