C++11 標(biāo)準(zhǔn)庫(kù)源代碼剖析:連載之四

Smart Pointers

Smart pointer,也就是所謂的“智能指針”,是指那些能夠自我管理生命周期的指針對(duì)象。C++ 11之前,標(biāo)準(zhǔn)庫(kù)只提供了一種智能指針,即std::auto_ptr。不過(guò)std::auto_ptr用處有限,大家更多地是使用boost智能指針。隨著時(shí)間推移,智能指針正在變得越來(lái)越流行,越來(lái)越重要,所以標(biāo)準(zhǔn)委員會(huì)在制定C++ 11標(biāo)準(zhǔn)時(shí),將boost智能指針也納入到標(biāo)準(zhǔn)中。

C++ 11標(biāo)準(zhǔn)庫(kù)提供了四種智能指針:

  • unique_ptr
  • shared_ptr
  • weak_ptr
  • auto_ptr

auto_ptr因?yàn)楣δ苡邢?,用處也有限,所以就不講了。至于weak_ptr,它的實(shí)現(xiàn)方式和shared_ptr很像,甚至大部分代碼是通用的,所以也不專(zhuān)門(mén)講了。下面我們重點(diǎn)分析一下unique_ptrshared_ptr。

unique_ptr

顧名思義,unique_ptr就是一個(gè)原生指針獨(dú)一無(wú)二的擁有者和管理者。當(dāng)一個(gè)unique_ptr離開(kāi)其作用域時(shí),其管理的原生指針會(huì)被自動(dòng)銷(xiāo)毀。

標(biāo)準(zhǔn)庫(kù)中定義了兩種類(lèi)型的unique_ptr

  1. unique_ptr<T>,管理通過(guò)new獲得的原生指針。
  2. unique_ptr<T[]>,管理通過(guò)new[]獲得的指針數(shù)組。

這兩種智能指針的實(shí)現(xiàn)方式大同小異,我們主要分析unique_ptr<T>的源碼。理解了unique_ptr<T>的實(shí)現(xiàn)原理,unique_ptr<T[]>的實(shí)現(xiàn)原理自然也就明了了。

unique_ptr的源代碼

// file: <memory>

template<class _Tp, class _Dp = default_delete<_Tp> >
class unique_ptr {
public:
    typedef _Tp element_type;
    typedef _Dp deleter_type;
    typedef typename __pointer_type<_Tp, deleter_type>::type pointer;

private:
    __compressed_pair<pointer, deleter_type> __ptr_;

    // ...
};

unique_ptr的聲明包含兩個(gè)模板參數(shù),第一個(gè)參數(shù)_Tp顯然就是原生指針的類(lèi)型。第二個(gè)模板參數(shù)_Dp是一個(gè)deleter,默認(rèn)值為default_delete<_Tp>default_delete是一個(gè)針對(duì)delete operator的函數(shù)對(duì)象:

// file: memory

template<class T>
struct default_delete {
    void operator()(T* ptr) const noexcept {
        delete ptr;
    }
};

注意這行代碼:

typedef typename __pointer_type<_Tp, deleter_type>::type pointer;

__pointer_type是一個(gè)type trait,用來(lái)“萃取”出正確的指針類(lèi)型。為了方便理解,大可以認(rèn)為它和下面的代碼是等價(jià)的:

typedef _Tp* pointer;

unique_ptr內(nèi)部用__compressed_pair保存數(shù)據(jù),__compressed_pair是一個(gè)“空基類(lèi)優(yōu)化”的pair,閱讀源代碼時(shí),完全可以將它當(dāng)做一個(gè)std::pair來(lái)對(duì)待。

這基本就是unique_ptr的全部聲明,下面我們來(lái)看如何構(gòu)造一個(gè)unique_ptr

// file: memory

template<class _Tp, class _Dp = default_delete<_Tp> >
class unique_ptr {
    // ...

public:
    // 默認(rèn)構(gòu)造函數(shù),調(diào)用pointer的默認(rèn)構(gòu)造函數(shù)
    inline constexpr unique_ptr() noexcept {
        : __ptr_(pointer()) {
    }

    // 將一個(gè)nullptr轉(zhuǎn)換為一個(gè)unique_ptr
    inline constexpr unique_ptr(nullptr_t) noexcept 
        : __ptr_(pointer()) {
    }

    // 拷貝構(gòu)造函數(shù),注意參數(shù)類(lèi)型為pointer,而不是const point&
    inline explicit unique_ptr(pointer __p) noexcept 
        : __ptr_(std::move(__p)){ 
    }

    // 移動(dòng)構(gòu)造函數(shù)
    inline unique_ptr<unique_ptr&& __u) noexcept
        : __ptr_(__u.release(), 
          std::forward<deleter_type>(__u.get_deleter())) {
    }

    // 移動(dòng)賦值
    inline unique_ptr& operator=(unique_ptr&& __u) noexcept {
        reset(__u.release());
        __ptr_.second() = std::forward<deleter_type>(__u.get_deleter());
        return *this;
    }

    inline ~unique_ptr(){reset();}

    // ...
};

unqiue_ptr還定義了兩個(gè)很重要的函數(shù):reset(pointer)release()。reset(pointer)的功能是用一個(gè)新指針替換原來(lái)的指針,而release()則是是放棄原生指針的所有權(quán)。

// file: memory

template<class _Tp, class _Dp = default_delete<_Tp> >
class unique_ptr {
    // ...

public:

    // 放棄對(duì)原生指針的所有權(quán),并返回原生指針
    inline pointer release() noexcept {
        pointer __t = __ptr_.first();
        __ptr_.first() = pointer();
        return __t;
    }

    // 用__p替換原生指針,被替換的指針最終被銷(xiāo)毀
    inline void reset(pointer __p = pointer()) noexcept {
        pointer __tmp = __ptr_.first();
        __ptr_.first() = __p;
        if (__tmp)
            __ptr_.second()(__tmp);
    }

到目前為止,unique_ptr還不像個(gè)指針,因?yàn)檫€缺少兩個(gè)方法:operator*operator->

// file: memory

template<class _Tp, class _Dp = default_delete<_Tp> >
class unique_ptr {
    // ...

public:
    inline add_lvalue_reference<_Tp>::type operator*() const {
        return *__ptr_.first();
    }

    inline pointer operator->() const noexcept {
        return __ptr_.first();
    }

這幾乎就是unique_ptr的全部源代碼了,總的來(lái)說(shuō)比較容易理解。下面我們來(lái)分析一個(gè)稍微復(fù)雜一些的智能指針:shared_ptr。

shared_ptr

還是先從聲明入手:

// file: memory

template<class _Tp>
class shared_ptr {
public:
    typedef _Tp element_type;

private:
    element_type *__ptr_;
    __shared_weak_count* __cntrl_;

    // ...
};

shared_ptr內(nèi)部維護(hù)了兩個(gè)指針:一個(gè)是被其管理原生指針__ptr_,還有一個(gè)類(lèi)型為__shared_weak_count的指針__cntrl_。那么這個(gè)__shared_weak_count又是什么呢?

file: memory

class __shared_count {
    // not copy constructible and not assignable
    __shared_count(const __shared_count&);
    __shared_count& operator=(const __shared_count&);

protected:
    long __shared_owners_; // how many owners do I have?
    virtual ~__shared_count();

public:
    explicit __shared_count(long __refs = 0) noexcept 
        : __shared_owners(__refs){}

        void __add_shared() noexcept;
        bool __release_shared() noexcept;
};

class __shared_weak_count : private __shared_count {
    long __shared_weak_owners_;

public:
    explicit __shared_weak_count(long __refs = 0) noexcept {
        : __shared_count(__refs), 
          __shared_weak_owners(__refs) {}

protected:
    virtual ~__shared_weak_count();

public:
    void __add_shared() noexcept;
    void __add_weak() noexcept;
    void __release_shared() noexcept;
    void __release_weak() noexcept;
    long use_count() const noexcept { return __shared_count::use_count();}

private:
    virtual void __on_zero_shared_weak() noexcept = 0;
};

__shared_weak_count是個(gè)虛基類(lèi),從它聲明的類(lèi)成員可以看出,這個(gè)類(lèi)的作用應(yīng)該是管理引用計(jì)數(shù)。實(shí)際上,shared_ptrweak_ptr內(nèi)部都聲明了__shared_weak_count*類(lèi)型的成員變量,也就是說(shuō),__shared_weak_count同時(shí)管理shared ownershared weak owner,我個(gè)人認(rèn)為這種做法值得商榷。

虛基類(lèi)的作用類(lèi)似于接口,是沒(méi)法直接使用的,所以還必須定義一個(gè)“實(shí)在”類(lèi):

// file: memory

template<class _Tp, class _Dp, class _Alloc>
class __shared_ptr_pointer : public __shared_weak_count {
    __compressed_pair<__compressed_pair<_Tp, _Dp>, _Alloc> __data_;

public:
    inline __shared_ptr_pointer(_Tp __P, _Dp __d, _Alloc __a)
        : __data_(__compressed_pair<_Tp, _Dp>(__p, std::move(__d)), std::move(__a)) {}

    // ...

};

后面我們會(huì)看到,__shared_ptr_pointer正是shared_ptr的大內(nèi)總管,不僅要記錄shared_ptrshared owner,還要負(fù)責(zé)分配內(nèi)存和銷(xiāo)毀指針等工作。所以__shared_ptr_pointer類(lèi)實(shí)際上有三個(gè)成員:

long __shared_owners_;
long __shared_weak_owners_;
__compressed_pair<__compressed_pair<_Tp, _Dp>, _Alloc> __data_;

理解了__shatrf_ptr_pointer,shared_ptr的源代碼就容易讀了:

// file: memory

template<class _Tp>>
class shared_ptr {
public:
    typedef _Tp element_type;

private:
    element_type*           __ptr_;
    __shared_weak_count*    __cntrl_;

    struct __nat{int __for_bool_;}; // placeholder

    // ...
};


// 如果 Yp* 能夠轉(zhuǎn)換成 _Tp*,則可以由 _Yp* 構(gòu)造一個(gè)shared_ptr<_Tp>
template<class _Tp>
template<class _Yp>
shared_ptr<_Tp>::shared_ptr(_Yp* __p,
        typename enable_if<is_convertible<_Yp*, element_type*>::value, __nat>::type)
        : __ptr_(__p) {
        unique_ptr<_Yp> __hold(__p);
        typedef __shared_ptr_pointer<_Yp*, default_delete<_Yp>, allocator<_Yp> >_CntrBlk;
        __cntrl_ = new _CntrBlk(__p, default_delete<_Yp>(), allocator<_Yp>());
        __hold.release();
}

// copy constructor, increment reference count
template<class _Tp>
inline shared_ptr<Tp>::shared_ptr(const shared_ptr& __r) noexcept
    : __ptr(__r.__ptr_), __cntrl_(__r.__cntrl_) {
    if (__cntrl_)
        __cntrl_->__add_shared();
}

// move constructor, does't increment reference count
template<class _Tp>
inline shared_ptr<T>::shared_ptr(shared_ptr&& __r) noexcept
    : __ptr_(__r.__ptr_), __cntrl_(__r.__cntrl) {
        __r.__ptr_ = 0;
        __r.__cntrl_ = 0;
}

template<class _Tp>
shared_ptr<_Tp>::~shared_ptr(){
    if (__cntrl_)
        __cntrl_->__release_shared();
}

shared_ptr的實(shí)現(xiàn)雖然比unique_ptr復(fù)雜了一些,但是如果你能讀懂unique_ptr的源代碼,那shared_ptr的源代碼對(duì)你來(lái)說(shuō)也不算個(gè)事。因?yàn)槠年P(guān)系,對(duì)shared_ptr的分析就到這里。最后順便說(shuō)說(shuō)一個(gè)關(guān)于效率的話(huà)題,我們已經(jīng)看到了,shared_ptr內(nèi)部維護(hù)了兩個(gè)指針,如果你直接調(diào)用構(gòu)造函數(shù),就想這樣:

class Widget;

auto sp = shared_ptr<Widget>(new Widget());

這里實(shí)際分配了兩次內(nèi)存,第一次是調(diào)用new Widget()的時(shí)候,第二次則是在shared_ptr構(gòu)造函數(shù)的內(nèi)部構(gòu)造__cntrl_的時(shí)候。分配內(nèi)存是很昂貴的操作,所以標(biāo)準(zhǔn)庫(kù)提供了make_shared()函數(shù),讓你一次分配全部所需的內(nèi)存:

// file: memory

template<class _Tp, class ..._Args>
inline
typename enable_if<!is_array<_Tp>::value, shared_ptr<_Tp>>::type
make_shared(_Args&& ...__args) {
    return shared_ptr<_Tp>::make_shared(std::forward<_Args>(__args)...);
}

template<class _Tp>
template<class ...Args>
shared_ptr<_Tp> shared_ptr<_Tp>::make_shared(_Args&& ...__args) {
    typedef __shared_ptr_emplace<_Tp, allocator<_Tp>>_CntrlBlk;
    typedef allocator<_CntrlBlk> _A2;
    typedef __alocator_destructor<_A2> _D2;

    _A2 __a2;
    unique_ptr<_CntrlBlk, _D2> __hold2(__a2.allocate(1), _D2(__a2, 1));
    ::new(__hold2.get()) _CntrlBlk(__a2, std::forward<_Args>(_args)...);
    shared_ptr<_Tp> __r;
    __r.__ptr_ = __hold2.get()->get();
    __r.__cntrl_ = __hold2.release();
    return __r;
}

我們可以看到,確實(shí)只分配了一次內(nèi)存。注意內(nèi)存的分配是在這里:

unique_ptr<_CntrlBlk, _D2> __hold2(__a2.allocate(1), _D2(__a2, 1));

而不是:

::new(__hold2.get()) _CntrlBlk(__a2, std::forward<_Args>(_args)...);

這里是調(diào)用placement new, 在__hold2的地址上構(gòu)造一個(gè)__CntrBlk。__CntrBlk的類(lèi)型是__shared_ptr_emplace<T, Alloc>,它的定義如下:

// file: memory

template<class _Tp, class _Alloc>
class __shared_ptr_emplace : public __shared_weak_count {
    __compressed_pair<_Alloc, _Tp> __data_;

public:
    template<class ..._Args>
    __shared_ptr_emplace(_Alloc __a, _Args&& ...__args)
        : __data_(piecewise_construct, std::forward_as_typle(__a),
        std::forward_as_tuple(std::forward<_Args>(__args)...)){}

    // ...
};

可見(jiàn)make_shared()shared_ptr的成員打包到一個(gè)__shared_ptr_emplace中,一次性在堆中構(gòu)造出一個(gè)__shared_ptr_emplace,然后再拆包分配給shared_ptr的成員變量。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀(guān)點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容