目录
在前面一文中,分析了auto_ptr和unique_ptr两种智能指针,二者都是独占式指针,即资源只能由一个智能指针拥有并管理。本文就来分析一下另外两种非独占式智能指针——共享式指针shared_ptr和weak_ptr。
共享式指针允许多个智能指针持有同一资源,并且当没有智能指针持有该资源时就自动销毁资源。为了实现共享+智能,就需要用到一个计数器,来记录某个资源被多少对象持有,当计数值为0时,就销毁该资源。
在shared_ptr和weak_ptr中,这样的计数器实际上是一个单独的类。它主要负责资源引用计数(增加、减少)以及资源的释放。此外,通过计数器,还能像unique_ptr那样指定删除器,还增加了一个空间分配器。下面就先来分析一下计数器:
计数器
计数器的根本,是一个抽象类_Ref_count_base,其定义如下:
class _Ref_count_base //管理计数变量 虚基类
{ // common code for reference counting
private:
virtual void _Destroy() = 0;
virtual void _Delete_this() = 0;
private:
_Atomic_counter_t _Uses; //引用计数
_Atomic_counter_t _Weaks; //弱引用计数
protected:
_Ref_count_base() //初始化两个计数变量为1
{ // construct
_Init_atomic_counter(_Uses, 1);
_Init_atomic_counter(_Weaks, 1);
}
public:
virtual ~_Ref_count_base() _NOEXCEPT
{ // ensure that derived classes can be destroyed properly
}
......
unsigned int _Get_uses() const //返回引用计数
{ // return use count
return (_Get_atomic_count(_Uses));
}
void _Incref()
{ // increment use count
_MT_INCR(_Mtx, _Uses);// _Uses+1
}
void _Incwref()
{ // increment weak reference count
_MT_INCR(_Mtx, _Weaks);//_Weaks+1
}
void _Decref()
{ // decrement use count
if (_MT_DECR(_Mtx, _Uses) == 0)
{ // destroy managed resource, decrement weak reference count
_Destroy();
_Decwref(); //如果资源已经被释放了,那么weak_ptr也没有任何作用了,
}
}
void _Decwref()
{ // decrement weak reference count
if (_MT_DECR(_Mtx, _Weaks) == 0) //如果_Weaks-1后为0,就调用_Delete_this
_Delete_this(); //释放当前对象
}
long _Use_count() const //返回引用计数
{ // return use count
return (_Get_uses());
}
bool _Expired() const //检测是否失效,失效是指引用计数为0
{ // return true if _Uses == 0
return (_Get_uses() == 0);
}
virtual void *_Get_deleter(const _XSTD2 type_info&) const
{ // return address of deleter object
return (0);
}
};
_Ref_count_base中主要包含以下信息:
1.两个纯虚函数_Destroy()和_Delete_this(),意味着二者必须在子类中实现,前者用来释放计数器对应的资源,后者用来释放计数器自身;
2.两个计数变量_Uses和_Weaks,前者是强引用计数,主要用于shared_ptr中,后者用于weak_ptr中;
3.定义了对_Uses和_Weaks的增加、减少函数。如果某一次减少使得_Uses为0,那么就会调用子类中实现的_Destroy函数并且减少_Weaks;如果某一次减少使得_Weaks为0,就会调用子类中实现的_Delete_this函数;
4._Ref_count_base的构造会初始化_Uses和_Weaks为1;
5._Uses和_Weaks的增加、减少都是原子操作。
显然,作为一个抽象类,不可能仅仅通过_Ref_count_base来实现计数器的,真正的计数器是子类来实现,_Ref_count_base的子类有三种:_Ref_count、_Ref_count_del和_Ref_count_del_alloc。从子类名也可以看出来,从前往后三种子类的功能更强大,体现在计数、删除器和分配器三个方面。
_Ref_count
_Ref_count就是一种计数器,它内部的成员变量只含一个资源指针_Ptr,其定义如下:
template<class _Ty>
class _Ref_count
: public _Ref_count_base //父类中管理计数变量,_Ref_count中管理其相应的资源
{ // handle reference counting for object without deleter
public:
_Ref_count(_Ty *_Px)
: _Ref_count_base(), _Ptr(_Px)
{ // construct
}
private:
virtual void _Destroy() //重写虚基类中的_Destroy
{ // destroy managed resource
delete _Ptr; //释放资源
}
virtual void _Delete_this()
{ // destroy self
delete this; //销毁当前对象
}
_Ty * _Ptr; //资源指针
};
该类中主要包含以下信息:
1._Ref_count中含有一个资源指针_Ptr,指向该计数器对应的资源;
2._Ref_count从_Ref_count_base_继承来的计数变量相关操作,都是针对_Ptr的;
3._Ref_count类中必须重写_Destroy和_Delete_this函数,前者对_Ptr进行释放,后者用来析构当前计数器对象。由于_Ref_count_base_的析构函数是虚函数,因此_Ref_count可以完全析构;
4._Ref_count类只接受用资源指针构造。
_Ref_count_del
_Ref_count_del是第二种计数器,它除了拥有资源指针外,还拥有一个删除器实例,如下所示:
template<class _Ty,
class _Dx>
class _Ref_count_del
: public _Ref_count_base //父类中管理计数变量,_Ref_count_del中管理相应的资源指针和删除器
{ // handle reference counting for object with deleter
public:
_Ref_count_del(_Ty *_Px, _Dx _Dt)
: _Ref_count_base(), _Ptr(_Px), _Dtor(_Dt)
{ // construct
}
virtual void *_Get_deleter(const _XSTD2 type_info& _Typeid) const
{ // return address of deleter object
return ((void *)(_Typeid == typeid(_Dx) ? &_Dtor : 0));
}
private:
virtual void _Destroy()
{ // destroy managed resource
_Dtor(_Ptr);
}
virtual void _Delete_this()
{ // destroy self
delete this;
}
//资源指针以及删除器
_Ty * _Ptr;
_Dx _Dtor; // the stored destructor for the controlled object
};
该类主要包含以下信息:
1._Ref_count_del除了有一个资源指针_Ptr,还有一个_Dx类型的删除器实例_Dtor;
2._Ref_count_del从_Ref_count_base_继承来的计数变量相关操作,都是针对_Ptr的;
3.重写的_Destroy函数中,并不像_Ref_count那样直接调用delete删除,而是使用的_Dtor(_Ptr),这说明删除器必须是一个仿函数类,_Delete_this和上述相同;
4._Ref_count_del只接受用资源指针和删除器实例来构造。
_Ref_count_del_alloc
最后一种计数器,除了包含一个删除器实例,还包含一个分配器实例,如下所示:
template<class _Ty,
class _Dx,
class _Alloc>
class _Ref_count_del_alloc
: public _Ref_count_base //父类中管理计数变量,_Ref_count_del_alloc中管理相应的资源指针、删除器和分配器
{ // handle reference counting for object with deleter and allocator
public:
typedef _Ref_count_del_alloc<_Ty, _Dx, _Alloc> _Myty;
typedef typename _Alloc::template rebind<_Myty>::other _Myalty;
_Ref_count_del_alloc(_Ty *_Px, _Dx _Dt, _Myalty _Al)
: _Ref_count_base(), _Ptr(_Px), _Dtor(_Dt), _Myal(_Al)
{ // construct
}
virtual void *_Get_deleter(const _XSTD2 type_info& _Typeid) const
{ // return address of deleter object
return ((void *)(_Typeid == typeid(_Dx) ? &_Dtor : 0));//如果传入的类型与删除器类型相同,就返回删除器地址
}
private:
virtual void _Destroy()
{ // destroy managed resource
_Dtor(_Ptr); //将资源指针作为参数调用删除器
}
virtual void _Delete_this()
{ // destroy self
_Myalty _Al = _Myal;
_Al.destroy(this);
_Al.deallocate(this, 1);
}
//资源指针、删除器、分配器
_Ty * _Ptr;
_Dx _Dtor; // the stored destructor for the controlled object
_Myalty _Myal; // the stored allocator for this
}
该类主要包含以下信息:
1._Ref_count_del_alloc增加了一个分配器实例;
2._Ref_count_del_alloc从_Ref_count_base_继承来的计数变量相关操作,都是针对_Ptr的;
3.重写的_Destroy函数中,用仿函数类实例_Dtor来销毁资源指针,而在重写的_Delete_this中,则是通过分配器实例来进行的,可见,分配器中必须实现destroy函数和deallocate函数(这就很像STL中的allocator);
4._Ref_count_del只接受用资源指针、删除器实例和分配器实例来构造。
可以看到,计数器实际上是通过_Ref_count_base的三个子类来实现的,这三种子类中都包含了一个资源指针,调用的destroy函数也是用来删除资源指针所指向的“资源”,可以理解为,计数器实际上就已经绑定到了某一处资源上。从这一点上,我们也大致能猜出,shared_ptr和weak_ptr之所以能和计数器对应起来,就是靠同一处资源。
_Ptr_base
在分析shared_ptr和weak_ptr之前,需要先分析另一个很重要的类——_Ptr_base。shared_ptr和weak_ptr都继承自它,现在来看看这个类的定义。