1.boost::scoped_ptr
作用域指针。关于这个指针没有什么太多好分析的,就是一个类里面接受一个指针类型,在类的生存周期结束时释放存放的指针
2.boost::scoped_array 数组智能指针。这个就比较有意思了 首先看一下构造函数,他只有一个显示构造
typedef T element_type;
explicit scoped_array( T * p = 0 ) BOOST_SP_NOEXCEPT : px( p )
{
#if defined(BOOST_SP_ENABLE_DEBUG_HOOKS)
boost::sp_array_constructor_hook( px );
#endif
}
首先,这个类只能接受一个new出来的数组,换言之,也就是堆上的数据,所以这个模板依赖的构造函数接受一个T*的数据,而不能接受如下类型
template<typename T,int N>scped_array(T[N])...
至于这个sp_array_constructor_hook暂时还没看到。
3.boost::shared_ptr 这个就比较经典了,目前c++11也引入了std::shared_ptr
typedef typename boost::detail::sp_element< T >::type element_type;
//这段代码,首先会进行一个类型的decay,比如int a[10] decay为int* ,int&a decay为int
BOOST_CONSTEXPR shared_ptr() BOOST_SP_NOEXCEPT : px( 0 ), pn()
{
}
//这个是默认构造函数,BOOST_CONSTEXPR这个声明这个构造为常量表达式,方便在编译器能够运行,px指向存储的单元的位置,pn为引用计数,BOOST_SP_NOEXCEPT指的是这个构造函数不会抛出异常
再来看下一个构造函数
template<class Y>
explicit shared_ptr( Y * p ): px( p ), pn() // Y must be complete
{
boost::detail::sp_pointer_construct( this, p, pn );
}
//首先,Y必须是完整类型,当然具体在模板中完整类型和非完整类型可以看我c++模板的文章
//思考一个问题如果Y为boost::shared_ptr*,会有问题吗?答案是不会有问题
//在这里pn会默认初始化,没有问题,pn有默认构造函数
//再来看boost::detail::sp_pointer_construct这个函数
template< class T, class Y > inline void sp_pointer_construct( boost::shared_ptr< T > * ppx, Y * p, boost::detail::shared_count & pn )
{
boost::detail::shared_count( p ).swap( pn );
boost::detail::sp_enable_shared_from_this( ppx, p, p );
}
//ok,我们先看boost::detail::shared_count( p ).swap( pn );这个shared_count和swap具体做了什么事情。
1.会将这个数据指针保存在一个sp_counted_impl_p类中注:sp_counted_impl_p是sp_counted_base派生类,sp_counted_base会初始化引用计数为1,weak_count也为1
2.swap做的事情就是交换sp_counted_base指针,因为这里面就是存放引用计数的类
//再来看boost::detail::sp_enable_shared_from_this( ppx, p, p );这个函数做了什么
template< class X, class Y, class T > inline void sp_enable_shared_from_this( boost::shared_ptr<X> const * ppx, Y const * py, boost::enable_shared_from_this< T > const * pe )
{
if( pe != 0 )
{
pe->_internal_accept_owner( ppx, const_cast< Y* >( py ) );
}
}
//在这里会调用inline void sp_enable_shared_from_this( ... )
{
}
当然这里暂时先不解释boost::enable_shared_from_this做了什么,后面解释
ok我们这里大概就明白初始化boost::shared_ptr会发生什么了
接下来看看拷贝构造函数
template<class Y>
#if !defined( BOOST_SP_NO_SP_CONVERTIBLE )
shared_ptr( shared_ptr<Y> const & r, typename boost::detail::sp_enable_if_convertible<Y,T>::type = boost::detail::sp_empty() )
#else
shared_ptr( shared_ptr<Y> const & r )
#endif
BOOST_SP_NOEXCEPT : px( r.px ), pn( r.pn )
{
boost::detail::sp_assert_convertible< Y, T >();
}
1.首先看一下
template< class Y, class T > struct sp_enable_if_convertible: public sp_enable_if_convertible_impl< sp_convertible< Y, T >::value >
{
};
ok,接着看一下sp_enable_if_convertible_impl发现这是定义的一个萃取,判断值是能否转化true或false
重点来了, sp_convertible< Y, T >::value
1.在这个模板结构体中,全特化了多个数组引用的版本,我们这里重点看一下非数组的情况代码如下
template< class Y, class T > struct sp_convertible
{
typedef char (&yes) [1];
typedef char (&no) [2];
static yes f( T* );
static no f( ... );
enum _vt { value = sizeof( (f)( static_cast<Y*>(0) ) ) == sizeof(yes) };
};
//ok,这里解释每一行代码,定义了一个名为yes,char[1]的数组,定义了一个名为no,char[2]的数组, static yes f( T* );
static no f( ... );
//这里是调用的时候如果第一个失败,不会报错(sfinae)
//最后一行就是能否转化,然后给value赋值
所以现在整体解释一下shared_ptr( shared_ptr<Y> const & r, typename boost::detail::sp_enable_if_convertible<Y,T>::type = boost::detail::sp_empty() )
//拷贝构造接受一个shared_ptr<Y>的参数,但是这个参数需要能转换为T,如果不能转换,boost::detail::sp_enable_if_convertible<Y,T>::type这个类型都不会有,换言之会被sfinae掉,但是没有另外的重载,所以会直接报错
在讲operator=之前,先讲一下移动构造,还是直接上代码
struct A {
};
template<typename T>
class B {
public:
};
template<typename T>class B<T*> {
public:
T*m_value =nullptr;
B() {
m_value = new T;
}
B(B&& b) {//移动构造,#1
this->m_value = b.m_value;
b.m_value = nullptr;
}
~B() {
if (m_value != nullptr) {
delete m_value;
m_value = nullptr;
}
}
};
//我们重点看一下#1,我们知道在拷贝构造函数中,有深浅拷贝问题,什么是深浅拷贝问题呢,就是两个指针指向同一个地址,那么析构的时候会析构两次报错,但是移动构造完美解决了问题,从#1的代码可以看出来,只有一个指针指向内存地址。
ok,那么我们进入operator= 上代码
shared_ptr & operator=( shared_ptr const & r ) BOOST_SP_NOEXCEPT
{
this_type(r).swap(*this);
return *this;
}
1.首先这里调用this_type(r),拷贝构造生成一个临时变量
代码如下
shared_ptr( shared_ptr const & r ) BOOST_SP_NOEXCEPT : px( r.px ), pn( r.pn )
{
}
ok,在这里boost::shared_count也会调用拷贝构造代码如下
shared_count(shared_count const & r) BOOST_SP_NOEXCEPT: pi_(r.pi_)
#if defined(BOOST_SP_ENABLE_DEBUG_HOOKS)
, id_(shared_count_id)
#endif
{
if( pi_ != 0 ) pi_->add_ref_copy();
}
当然不用我说,你也发现了在这里如果对象还没有被析构,那么引用计数增加
再看swap函数
void swap( shared_ptr & other ) BOOST_SP_NOEXCEPT
{
std::swap(px, other.px);
pn.swap(other.pn);
}
首先我们知道这是一个临时变量调用的swap,那么也就是说这个变量的生存周期就在这一行,
然后看一下std::swap的定义
_CONSTEXPR20 void swap(_Ty& _Left, _Ty& _Right) noexcept(
is_nothrow_move_constructible_v<_Ty>&& is_nothrow_move_assignable_v<_Ty>) {
_Ty _Tmp = _STD move(_Left);
_Left = _STD move(_Right);
_Right = _STD move(_Tmp);
}
这里会调用_Ty的移动构造,具体移动构造干了什么,看前面我给出的例子
欧克,再看一下pn.swap(other.pn);具体定义如下
void swap(shared_count & r) BOOST_SP_NOEXCEPT
{
sp_counted_base * tmp = r.pi_;
r.pi_ = pi_;
pi_ = tmp;
}
这里可以看到,直接交换了指针的值,同样的效果,但是之所以不用std::move的原因来处理,应该是因为这个sp_counted_base是在类中申请的空间,后面需要单独释放,而不能直接写为nullptr;之前的px是用户申请的一个指针,只保存一份
最后再来看一下析构的过程
~shared_count() /*BOOST_SP_NOEXCEPT*/
{
if( pi_ != 0 ) pi_->release();
#if defined(BOOST_SP_ENABLE_DEBUG_HOOKS)
id_ = 0;
#endif
}
//这是类的析构函数
可以看到首先判断了类型是否为空,也就是说现在即使在外部获取指针释放了,也不会有影响
再来看看pi_->release();
里面做了两件事情,第一件事情是判断引用计数是否为0,如果这件事情满足,那么第二件事情就是释放对象
4.weak_ptr
template<class Y>
#if !defined( BOOST_SP_NO_SP_CONVERTIBLE )
weak_ptr( shared_ptr<Y> const & r, typename boost::detail::sp_enable_if_convertible<Y,T>::type = boost::detail::sp_empty() )
#else
weak_ptr( shared_ptr<Y> const & r )
#endif
BOOST_SP_NOEXCEPT : px( r.px ), pn( r.pn )
{
boost::detail::sp_assert_convertible< Y, T >();
}
//通过构造函数可以看出弱智能指针,必须通过共享指针初始化。在这里面也有一个引用计数变量pn.pn的类型也是shared_count
//代码如下,返回一个shared_ptr对象
那么深入这个对象去看一看
inline shared_count::shared_count( weak_count const & r, sp_nothrow_tag ) BOOST_SP_NOEXCEPT: pi_( r.pi_ )
#if defined(BOOST_SP_ENABLE_DEBUG_HOOKS)
, id_(shared_count_id)
#endif
{
if( pi_ != 0 && !pi_->add_ref_lock() )
{
pi_ = 0;
}
}
//可以看到如果对象不为空,并且可以上锁给引用计数加1,那么就将原对象变为空
//那整个流程还是比较清晰了,就是lock之后会生成一个新的shared_ptr并且引用计数+1
5.关于std::unique_ptr简单介绍一下