std::shared_ptr 学习笔记
shared_ptr 是多个属主指向同一个对象的智能指针。shared_ptr 可以被拷贝,也可以通过值传递的方式作为参数传递给函数。
shared_ptr 通过引用计数来维护对象的生命周期,当新添加指向该对象的智能指针时,引用计数加 1,当有一个指向该对象的智能指针析构时,引用计数减 1。当引用计数变为 0 时,对象内存会被释放掉。

创建 shared_ptr 可以使用帮助函数 make_shared 来创建。
Examples
- 依赖的代码 - // shared_ptr-examples.cpp 
 // The following examples assume these declarations:
 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> | 
以下图片展示了一个指向 int 类型的一个 shared_ptr,其在 x86 编译下大小为 8。
| 
 | 

引用计数类 _Ref_count_base
引用计数在智能指针的用法是当有新的 shared_ptr 共享对象时,对象的引用计数加 1,当有指向对象的指针释放时,引用计数减 1.
在引用计数类中,有两个成员变量,一个是引用的数量,一个是 weak 使用的数量。
| using _Atomic_counter_t = unsigned long; | 
引用计数类支持一些对计数的操作
| void _Incref() noexcept { // increment use count | 
指针基类 _Ptr_base
_Ptr_base 实现了 shared_ptr 和 weak_ptr 的基本操作。
在 _Ptr_base 基类中,有两个成员变量,底层指针 _Ptr 和引用计数类 _Rep
| element_type* _Ptr{nullptr}; | 
然后定义了对指针和引用计数的操作
| // 引用计数加一 | 
基类指针中还定义了一些构造函数
| // 禁用掉拷贝构造函数和拷贝赋值函数 | 
shared_ptr 类
shared_ptr 模板类继承于基类指针
| template <class _Ty> | 
构造函数和析构函数。由于代码过长,使用点省略部分内容,同时具体实现未放进来,具体可以参考 MSVC 的源码。
| constexpr shared_ptr() noexcept = default; | 
对于 deleter,是在释放资源时,调用 deleter(ptr),其中 deleter 是删除函数,ptr 是底层指针。
由于 shared_ptr 允许拷贝和移动,所以实现了对应的拷贝和移动函数
| // 拷贝构造函数 | 
shared_ptr 支持交换和重置底层指针
| void swap(shared_ptr& _Other) noexcept { | 
shared_ptr 和普通指针一样,也支持 * 运算符和 → 运算符。对于数组类型的共享指针,还支持 [] 运算符
| template <class _Ty2 = _Ty, ...> | 
同时,为了方便指针判断是否为空,实现了 operator bool 运算。同时可以通过 unique() 来判断是否引用计数为 1
| _CXX17_DEPRECATE_SHARED_PTR_UNIQUE _NODISCARD bool unique() const noexcept { | 

