智能指针
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类;