第一章 boost中智能指针源码分析

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发现这是定义的一个萃取,判断值是能否转化truefalse
重点来了, 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简单介绍一下