shared_ptr 是多个属主指向同一个对象的智能指针。shared_ptr 可以被拷贝,也可以通过值传递的方式作为参数传递给函数。

shared_ptr 通过引用计数来维护对象的生命周期,当新添加指向该对象的智能指针时,引用计数加 1,当有一个指向该对象的智能指针析构时,引用计数减 1。当引用计数变为 0 时,对象内存会被释放掉。

shared_ptr0

创建 shared_ptr 可以使用帮助函数 make_shared 来创建。

Examples

  • 依赖的代码

    // shared_ptr-examples.cpp
    // The following examples assume these declarations:
    #include <algorithm>
    #include <iostream>
    #include <memory>
    #include <string>
    #include <vector>

    struct MediaAsset
    {
    virtual ~MediaAsset() = default; // make it polymorphic
    };

    struct Song : public MediaAsset
    {
    std::wstring artist;
    std::wstring title;
    Song(const std::wstring& artist_, const std::wstring& title_) :
    artist{ artist_ }, title{ title_ } {}
    };

    struct Photo : public MediaAsset
    {
    std::wstring date;
    std::wstring location;
    std::wstring subject;
    Photo(
    const std::wstring& date_,
    const std::wstring& location_,
    const std::wstring& subject_) :
    date{ date_ }, location{ location_ }, subject{ subject_ } {}
    };

    using namespace std;

    int main()
    {
    // The examples go here, in order:
    // Example 1
    // Example 2
    // Example 3
    // Example 4
    // Example 6
    }
  • 创建 shared_ptr 方法

    // Use make_shared function when possible.
    auto sp1 = make_shared<Song>(L"The Beatles", L"Im Happy Just to Dance With You");

    // Ok, but slightly less efficient.
    // Note: Using new expression as constructor argument
    // creates no named variable for other code to access.
    shared_ptr<Song> sp2(new Song(L"Lady Gaga", L"Just Dance"));

    // When initialization must be separate from declaration, e.g. class members,
    // initialize with nullptr to make your programming intent explicit.
    shared_ptr<Song> sp5(nullptr);
    //Equivalent to: shared_ptr<Song> sp5;
    //...
    sp5 = make_shared<Song>(L"Elton John", L"I'm Still Standing");
  • shared_ptr 的拷贝,复制等使用

    //Initialize with copy constructor. Increments ref count.
    auto sp3(sp2);

    //Initialize via assignment. Increments ref count.
    auto sp4 = sp2;

    //Initialize with nullptr. sp7 is empty.
    shared_ptr<Song> sp7(nullptr);

    // Initialize with another shared_ptr. sp1 and sp2
    // swap pointers as well as ref counts.
    sp1.swap(sp2);
  • shared_ptr 用于 container 和 algorithm

    vector<shared_ptr<Song>> v {
    make_shared<Song>(L"Bob Dylan", L"The Times They Are A Changing"),
    make_shared<Song>(L"Aretha Franklin", L"Bridge Over Troubled Water"),
    make_shared<Song>(L"Thalía", L"Entre El Mar y Una Estrella")
    };

    vector<shared_ptr<Song>> v2;
    remove_copy_if(v.begin(), v.end(), back_inserter(v2), [] (shared_ptr<Song> s)
    {
    return s->artist.compare(L"Bob Dylan") == 0;
    });

    for (const auto& s : v2)
    {
    wcout << s->artist << L":" << s->title << endl;
    }
  • shared_ptr cast

    vector<shared_ptr<MediaAsset>> assets {
    make_shared<Song>(L"Himesh Reshammiya", L"Tera Surroor"),
    make_shared<Song>(L"Penaz Masani", L"Tu Dil De De"),
    make_shared<Photo>(L"2011-04-06", L"Redmond, WA", L"Soccer field at Microsoft.")
    };

    vector<shared_ptr<MediaAsset>> photos;

    copy_if(assets.begin(), assets.end(), back_inserter(photos), [] (shared_ptr<MediaAsset> p) -> bool
    {
    // Use dynamic_pointer_cast to test whether
    // element is a shared_ptr<Photo>.
    shared_ptr<Photo> temp = dynamic_pointer_cast<Photo>(p);
    return temp.get() != nullptr;
    });

    for (const auto& p : photos)
    {
    // We know that the photos vector contains only
    // shared_ptr<Photo> objects, so use static_cast.
    wcout << "Photo location: " << (static_pointer_cast<Photo>(p))->location << endl;
    }
  • shared_ptr 作为函数参数传递

    • 通过传值方式进行传递。会调用拷贝构造函数,增加引用计数,使被调用者成为属主。
    • 通过引用或者常引用进行传递。此时,引用计数不会增加,只要调用者没有超出 scope,被调用者可以使用该指针。或者,被调用者可以根据传入的引用创建一个 shared_ptr,然后成为共享属主。
    • 传入底层指针或者底层对象的引用。被调用者允许使用对象,但是不能共享属主关系。如果被调用者使用底层指针构建新的 shared_ptr,新建的指针不依赖于原始指针,不能控制底层资源。

代码解读(MSVC SDK 10.0.19041.0)

shared_ptr 是类模板,其原型为

template< class T > class shared_ptr;

在 MSVC 的实现中,带有引用计数的类是在一个基类中实现的,然后 shared_ptr 和 weak_ptr 继承该基类。

template <class _Ty>
class _Ptr_base { // base class for shared_ptr and weak_ptr

template <class _Ty>
class shared_ptr : public _Ptr_base<_Ty> { // class for reference counted resource

以下图片展示了一个指向 int 类型的一个 shared_ptr,其在 x86 编译下大小为 8。

#include <iostream>
#include <memory>

int main(void)
{
std::shared_ptr<int> ps = std::make_shared<int>();
std::cout << sizeof(ps) << '\n'; // output is 8 in x86 debug
return 0;
}

shared_ptr1

引用计数类 _Ref_count_base

引用计数在智能指针的用法是当有新的 shared_ptr 共享对象时,对象的引用计数加 1,当有指向对象的指针释放时,引用计数减 1.

在引用计数类中,有两个成员变量,一个是引用的数量,一个是 weak 使用的数量。

using _Atomic_counter_t = unsigned long;

_Atomic_counter_t _Uses = 1;
_Atomic_counter_t _Weaks = 1;

引用计数类支持一些对计数的操作

void _Incref() noexcept { // increment use count
_MT_INCR(_Uses);
}

void _Incwref() noexcept { // increment weak reference count
_MT_INCR(_Weaks);
}

// 当引用计数减为 0 时销毁对象
void _Decref() noexcept { // decrement use count
if (_MT_DECR(_Uses) == 0) {
_Destroy();
_Decwref();
}
}

void _Decwref() noexcept { // decrement weak reference count
if (_MT_DECR(_Weaks) == 0) {
_Delete_this();
}
}

long _Use_count() const noexcept {
return static_cast<long>(_Uses);
}

virtual void* _Get_deleter(const type_info&) const noexcept {
return nullptr;
}

指针基类 _Ptr_base

_Ptr_base 实现了 shared_ptr 和 weak_ptr 的基本操作。

在 _Ptr_base 基类中,有两个成员变量,底层指针 _Ptr 和引用计数类 _Rep

element_type* _Ptr{nullptr};
_Ref_count_base* _Rep{nullptr};

然后定义了对指针和引用计数的操作

// 引用计数加一
void _Incref() const noexcept {
if (_Rep) {
_Rep->_Incref();
}
}

// 引用计数减一
void _Decref() noexcept { // decrement reference count
if (_Rep) {
_Rep->_Decref();
}
}

// 弱引用计数加一
void _Incwref() const noexcept {
if (_Rep) {
_Rep->_Incwref();
}
}

// 弱引用计数减一
void _Decwref() noexcept { // decrement weak reference count
if (_Rep) {
_Rep->_Decwref();
}
}

// 获取引用计数
_NODISCARD long use_count() const noexcept {
return _Rep ? _Rep->_Use_count() : 0;
}

// 获取底层指针
_NODISCARD element_type* get() const noexcept {
return _Ptr;
}

基类指针中还定义了一些构造函数

// 禁用掉拷贝构造函数和拷贝赋值函数
_Ptr_base(const _Ptr_base&) = delete;
_Ptr_base& operator=(const _Ptr_base&) = delete;

// 使用默认的构造函数和析构函数
constexpr _Ptr_base() noexcept = default;
~_Ptr_base() = default;

// 通过定义辅助函数来实现移动,移动后右指针变为空
template <class _Ty2>
void _Move_construct_from(_Ptr_base<_Ty2>&& _Right) noexcept {
// implement shared_ptr's (converting) move ctor and weak_ptr's move ctor
_Ptr = _Right._Ptr;
_Rep = _Right._Rep;

_Right._Ptr = nullptr;
_Right._Rep = nullptr;
}

// 通过定义辅助函数来实现 hared_ptr 的拷贝,拷贝后,引用计数加一
template <class _Ty2>
void _Copy_construct_from(const shared_ptr<_Ty2>& _Other) noexcept {
// implement shared_ptr's (converting) copy ctor
_Other._Incref();

_Ptr = _Other._Ptr;
_Rep = _Other._Rep;
}

template <class _Ty2>
void _Alias_construct_from(const shared_ptr<_Ty2>& _Other, element_type* _Px) noexcept {
// implement shared_ptr's aliasing ctor
_Other._Incref();

_Ptr = _Px;
_Rep = _Other._Rep;
}

template <class _Ty2>
void _Alias_move_construct_from(shared_ptr<_Ty2>&& _Other, element_type* _Px) noexcept {
// implement shared_ptr's aliasing move ctor
_Ptr = _Px;
_Rep = _Other._Rep;

_Other._Ptr = nullptr;
_Other._Rep = nullptr;
}

template <class _Ty2>
bool _Construct_from_weak(const weak_ptr<_Ty2>& _Other) noexcept {
// implement shared_ptr's ctor from weak_ptr, and weak_ptr::lock()
if (_Other._Rep && _Other._Rep->_Incref_nz()) {
_Ptr = _Other._Ptr;
_Rep = _Other._Rep;
return true;
}

return false;
}

template <class _Ty2>
void _Weakly_construct_from(const _Ptr_base<_Ty2>& _Other) noexcept { // implement weak_ptr's ctors
if (_Other._Rep) {
_Ptr = _Other._Ptr;
_Rep = _Other._Rep;
_Rep->_Incwref();
} else {
_STL_INTERNAL_CHECK(!_Ptr && !_Rep);
}
}

template <class _Ty2>
void _Weakly_convert_lvalue_avoiding_expired_conversions(const _Ptr_base<_Ty2>& _Other) noexcept {
// implement weak_ptr's copy converting ctor
if (_Other._Rep) {
_Rep = _Other._Rep; // always share ownership
_Rep->_Incwref();

if (_Rep->_Incref_nz()) {
_Ptr = _Other._Ptr; // keep resource alive during conversion, handling virtual inheritance
_Rep->_Decref();
} else {
_STL_INTERNAL_CHECK(!_Ptr);
}
} else {
_STL_INTERNAL_CHECK(!_Ptr && !_Rep);
}
}

template <class _Ty2>
void _Weakly_convert_rvalue_avoiding_expired_conversions(_Ptr_base<_Ty2>&& _Other) noexcept {
// implement weak_ptr's move converting ctor
_Rep = _Other._Rep; // always transfer ownership
_Other._Rep = nullptr;

if (_Rep && _Rep->_Incref_nz()) {
_Ptr = _Other._Ptr; // keep resource alive during conversion, handling virtual inheritance
_Rep->_Decref();
} else {
_STL_INTERNAL_CHECK(!_Ptr);
}

_Other._Ptr = nullptr;
}

shared_ptr 类

shared_ptr 模板类继承于基类指针

template <class _Ty>
class shared_ptr : public _Ptr_base<_Ty> { // class for reference counted resource management

构造函数和析构函数。由于代码过长,使用点省略部分内容,同时具体实现未放进来,具体可以参考 MSVC 的源码。

constexpr shared_ptr() noexcept = default;

constexpr shared_ptr(nullptr_t) noexcept {} // construct empty shared_ptr

template <class _Ux, ......>
explicit shared_ptr(_Ux* _Px) { // construct shared_ptr object that owns _Px

template <class _Ux, class _Dx, ......>
shared_ptr(_Ux* _Px, _Dx _Dt) { // construct with _Px, deleter

template <class _Ux, class _Dx, class _Alloc, ......>
shared_ptr(_Ux* _Px, _Dx _Dt, _Alloc _Ax) { // construct with _Px, deleter, allocator

template <class _Dx, ......>
shared_ptr(nullptr_t, _Dx _Dt) { // construct with nullptr, deleter

template <class _Dx, class _Alloc,.....>
shared_ptr(nullptr_t, _Dx _Dt, _Alloc _Ax) { // construct with nullptr, deleter, allocator

template <class _Ty2>
shared_ptr(const shared_ptr<_Ty2>& _Right, element_type* _Px) noexcept {
// construct shared_ptr object that aliases _Right
this->_Alias_construct_from(_Right, _Px);
}

template <class _Ty2>
shared_ptr(shared_ptr<_Ty2>&& _Right, element_type* _Px) noexcept {
// move construct shared_ptr object that aliases _Right
this->_Alias_move_construct_from(_STD move(_Right), _Px);
}

shared_ptr(const shared_ptr& _Other) noexcept { // construct shared_ptr object that owns same resource as _Other
this->_Copy_construct_from(_Other);
}

template <class _Ty2, ......>
shared_ptr(const shared_ptr<_Ty2>& _Other) noexcept {
// construct shared_ptr object that owns same resource as _Other
this->_Copy_construct_from(_Other);
}

shared_ptr(shared_ptr&& _Right) noexcept { // construct shared_ptr object that takes resource from _Right
this->_Move_construct_from(_STD move(_Right));
}

template <class _Ty2, ......>
shared_ptr(shared_ptr<_Ty2>&& _Right) noexcept { // construct shared_ptr object that takes resource from _Right
this->_Move_construct_from(_STD move(_Right));
}

template <class _Ty2, enable_if_t<_SP_pointer_compatible<_Ty2, _Ty>::value, int> = 0>
explicit shared_ptr(const weak_ptr<_Ty2>& _Other) { // construct shared_ptr object that owns resource *_Other
if (!this->_Construct_from_weak(_Other)) {
_Throw_bad_weak_ptr();
}
}

template <class _Ux, class _Dx,......>
shared_ptr(unique_ptr<_Ux, _Dx>&& _Other) {

~shared_ptr() noexcept { // release resource
this->_Decref();
}

对于 deleter,是在释放资源时,调用 deleter(ptr),其中 deleter 是删除函数,ptr 是底层指针。

由于 shared_ptr 允许拷贝和移动,所以实现了对应的拷贝和移动函数

// 拷贝构造函数
shared_ptr& operator=(const shared_ptr& _Right) noexcept {
shared_ptr(_Right).swap(*this);
return *this;
}

// 拷贝赋值函数
template <class _Ty2>
shared_ptr& operator=(const shared_ptr<_Ty2>& _Right) noexcept {
shared_ptr(_Right).swap(*this);
return *this;
}

// 移动赋值函数
shared_ptr& operator=(shared_ptr&& _Right) noexcept { // take resource from _Right
shared_ptr(_STD move(_Right)).swap(*this);
return *this;
}

// 对于底层指针类型兼容的移动赋值函数
template <class _Ty2>
shared_ptr& operator=(shared_ptr<_Ty2>&& _Right) noexcept { // take resource from _Right
shared_ptr(_STD move(_Right)).swap(*this);
return *this;
}

// auto_ptr 移动给 shared_ptr
template <class _Ty2>
shared_ptr& operator=(auto_ptr<_Ty2>&& _Right) {
shared_ptr(_STD move(_Right)).swap(*this);
return *this;
}

// unique_ptr 移动给 shared_ptr
template <class _Ux, class _Dx>
shared_ptr& operator=(unique_ptr<_Ux, _Dx>&& _Right) { // move from unique_ptr
shared_ptr(_STD move(_Right)).swap(*this);
return *this;
}

shared_ptr 支持交换和重置底层指针

void swap(shared_ptr& _Other) noexcept {
this->_Swap(_Other);
}

void reset() noexcept { // release resource and convert to empty shared_ptr object
shared_ptr().swap(*this);
}

template <class _Ux>
void reset(_Ux* _Px) { // release, take ownership of _Px
shared_ptr(_Px).swap(*this);
}

template <class _Ux, class _Dx>
void reset(_Ux* _Px, _Dx _Dt) { // release, take ownership of _Px, with deleter _Dt
shared_ptr(_Px, _Dt).swap(*this);
}

template <class _Ux, class _Dx, class _Alloc>
void reset(_Ux* _Px, _Dx _Dt, _Alloc _Ax) { // release, take ownership of _Px, with deleter _Dt, allocator _Ax
shared_ptr(_Px, _Dt, _Ax).swap(*this);
}

shared_ptr 和普通指针一样,也支持 * 运算符和 运算符。对于数组类型的共享指针,还支持 [] 运算符

template <class _Ty2 = _Ty, ...>
_NODISCARD _Ty2& operator*() const noexcept {
return *get();
}

template <class _Ty2 = _Ty, ...>
_NODISCARD _Ty2* operator->() const noexcept {
return get();
}

// 该函数只针对数组类型的指针使用
template <class _Ty2 = _Ty, class _Elem = element_type, enable_if_t<is_array_v<_Ty2>, int> = 0>
_NODISCARD _Elem& operator[](ptrdiff_t _Idx) const noexcept /* strengthened */ {
return get()[_Idx];
}

同时,为了方便指针判断是否为空,实现了 operator bool 运算。同时可以通过 unique() 来判断是否引用计数为 1

_CXX17_DEPRECATE_SHARED_PTR_UNIQUE _NODISCARD bool unique() const noexcept {
// return true if no other shared_ptr object owns this resource
return this->use_count() == 1;
}

explicit operator bool() const noexcept {
return get() != nullptr;
}

参考链接