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 { |