std::shared_ptr
定义在头文件 <memory> 中 |
||
template< class T > class shared_ptr; |
(自 C++11 起) | |
std::shared_ptr
是一种智能指针,通过指针保留对对象的共享所有权。多个 shared_ptr
对象可以拥有同一个对象。当以下任一情况发生时,对象将被销毁,其内存将被释放
该对象使用 delete 表达式 或在构造期间提供给 shared_ptr
的自定义删除器来销毁。
shared_ptr
可以共享对一个对象的拥有权,同时存储指向另一个对象的指针。此特性可用于指向成员对象,同时拥有它们所属的对象。存储的指针是通过 get()、解引用运算符和比较运算符访问的指针。当使用计数达到零时,传递给删除器的指针是托管指针。
shared_ptr
也可以不拥有任何对象,在这种情况下,它被称为空(如果使用别名构造函数创建了一个空 shared_ptr
,它可能具有一个非空存储指针)。
shared_ptr
的所有特化都满足 CopyConstructible、CopyAssignable 和 LessThanComparable 的要求,并且可以 隐式转换为 bool
。
所有成员函数(包括复制构造函数和复制赋值运算符)都可以被多个线程在不同的shared_ptr
对象上调用,而无需额外的同步,即使这些对象是副本并共享同一个对象的拥有权。如果多个执行线程在没有同步的情况下访问同一个shared_ptr
对象,并且任何访问都使用了shared_ptr
的非const成员函数,那么就会发生数据竞争;可以使用std::atomic<shared_ptr>来防止数据竞争。
目录 |
[edit] 成员类型
成员类型 | 定义 | ||||
element_type
|
| ||||
weak_type (自 C++17 起) |
std::weak_ptr<T> |
[edit] 成员函数
构造新的shared_ptr (公共成员函数) | |
如果不再有shared_ptr 链接到它,则析构被拥有的对象(公共成员函数) | |
赋值shared_ptr (公共成员函数) | |
修改器 | |
替换被管理的对象 (公共成员函数) | |
交换被管理的对象 (公共成员函数) | |
观察者 | |
返回存储的指针 (公共成员函数) | |
解除对存储的指针的引用 (公共成员函数) | |
(C++17) |
提供对存储的数组的索引访问 (公共成员函数) |
返回引用同一个被管理对象的shared_ptr 对象的数目(公共成员函数) | |
(直到 C++20) |
检查被管理的对象是否只由当前shared_ptr 对象管理(公共成员函数) |
检查存储的指针是否不为空 (公共成员函数) | |
提供基于所有权的共享指针排序 (公共成员函数) | |
(C++26) |
提供基于所有权的共享指针哈希 (公共成员函数) |
(C++26) |
提供基于所有权的共享指针相等比较 (公共成员函数) |
[edit] 非成员函数
创建一个管理新对象的共享指针 (函数模板) | |
创建一个管理使用分配器分配的新对象的共享指针 (函数模板) | |
将static_cast,dynamic_cast,const_cast或reinterpret_cast应用于存储的指针 (函数模板) | |
如果拥有,则返回指定类型的删除器 (函数模板) | |
(在 C++20 中移除)(在 C++20 中移除)(在 C++20 中移除)(在 C++20 中移除)(在 C++20 中移除)(C++20) |
与另一个shared_ptr 或nullptr 比较(函数模板) |
将存储的指针的值输出到输出流 (函数模板) | |
(C++11) |
专门化std::swap算法 (函数模板) |
专门化std::shared_ptr 的原子操作(函数模板) |
[edit] 辅助类
(C++20) |
原子共享指针 (类模板专门化) |
(C++11) |
对std::shared_ptr的哈希支持 (类模板专门化) |
[edit] 推导指南 (自 C++17 起)
[edit] 备注
对象的拥有权只能通过将它的值复制构造或复制赋值到另一个shared_ptr
来与另一个shared_ptr
共享。使用另一个shared_ptr
拥有的原始底层指针构造一个新的shared_ptr
会导致未定义的行为。
std::shared_ptr
可以与不完整类型T
一起使用。但是,从原始指针构造的构造函数(template<class Y> shared_ptr(Y*))以及template<class Y> void reset(Y*)成员函数只能用指向完整类型的指针调用(注意,std::unique_ptr可以从指向不完整类型的原始指针构造)。
在 std::shared_ptr<T> 中,T
可以是函数类型:在这种情况下,它管理的是指向函数的指针,而不是指向对象的指针。这有时用于使动态库或插件在它的任何函数被引用时保持加载。
void del(void(*)()) {} void fun() {} int main() { std::shared_ptr<void()> ee(fun, del); (*ee)(); }
[编辑] 实现说明
在典型实现中,shared_ptr
仅包含两个指针。
- 存储的指针(由 get() 返回);
- 指向控制块的指针。
控制块是动态分配的对象,它包含
- 指向受管对象的指针或受管对象本身;
- 删除器(类型擦除);
- 分配器(类型擦除);
- 拥有受管对象的
shared_ptr
数量; - 引用受管对象的
weak_ptr
数量。
当通过调用 std::make_shared 或 std::allocate_shared 创建 shared_ptr
时,控制块和受管对象的内存通过一次分配创建。受管对象在控制块的数据成员中就地构造。当通过 shared_ptr
构造函数之一创建 shared_ptr
时,受管对象和控制块必须分别分配。在这种情况下,控制块存储指向受管对象的指针。
shared_ptr
直接持有的指针是由 get() 返回的指针,而控制块持有的指针/对象是在共享所有者数量达到零时将被删除的指针/对象。这些指针不一定相等。
shared_ptr
的析构函数递减控制块的共享所有者数量。如果该计数器达到零,控制块将调用受管对象的析构函数。在 std::weak_ptr 计数器也达到零之前,控制块不会自释放。
在现有的实现中,如果存在指向相同控制块的共享指针,则弱指针的数量将递增 ([1],[2])。
为了满足线程安全要求,引用计数器通常使用相当于 std::atomic::fetch_add 的方法递增,并使用 std::memory_order_relaxed(递减需要更强的排序才能安全地销毁控制块)。
[编辑] 示例
#include <chrono> #include <iostream> #include <memory> #include <mutex> #include <thread> using namespace std::chrono_literals; struct Base { Base() { std::cout << "Base::Base()\n"; } // Note: non-virtual destructor is OK here ~Base() { std::cout << "Base::~Base()\n"; } }; struct Derived : public Base { Derived() { std::cout << "Derived::Derived()\n"; } ~Derived() { std::cout << "Derived::~Derived()\n"; } }; void print(auto rem, std::shared_ptr<Base> const& sp) { std::cout << rem << "\n\tget() = " << sp.get() << ", use_count() = " << sp.use_count() << '\n'; } void thr(std::shared_ptr<Base> p) { std::this_thread::sleep_for(987ms); std::shared_ptr<Base> lp = p; // thread-safe, even though the // shared use_count is incremented { static std::mutex io_mutex; std::lock_guard<std::mutex> lk(io_mutex); print("Local pointer in a thread:", lp); } } int main() { std::shared_ptr<Base> p = std::make_shared<Derived>(); print("Created a shared Derived (as a pointer to Base)", p); std::thread t1{thr, p}, t2{thr, p}, t3{thr, p}; p.reset(); // release ownership from main print("Shared ownership between 3 threads and released ownership from main:", p); t1.join(); t2.join(); t3.join(); std::cout << "All threads completed, the last one deleted Derived.\n"; }
可能的输出
Base::Base() Derived::Derived() Created a shared Derived (as a pointer to Base) get() = 0x118ac30, use_count() = 1 Shared ownership between 3 threads and released ownership from main: get() = 0, use_count() = 0 Local pointer in a thread: get() = 0x118ac30, use_count() = 5 Local pointer in a thread: get() = 0x118ac30, use_count() = 4 Local pointer in a thread: get() = 0x118ac30, use_count() = 2 Derived::~Derived() Base::~Base() All threads completed, the last one deleted Derived.
[编辑] 示例
#include <iostream> #include <memory> struct MyObj { MyObj() { std::cout << "MyObj constructed\n"; } ~MyObj() { std::cout << "MyObj destructed\n"; } }; struct Container : std::enable_shared_from_this<Container> // note: public inheritance { std::shared_ptr<MyObj> memberObj; void CreateMember() { memberObj = std::make_shared<MyObj>(); } std::shared_ptr<MyObj> GetAsMyObj() { // Use an alias shared ptr for member return std::shared_ptr<MyObj>(shared_from_this(), memberObj.get()); } }; #define COUT(str) std::cout << '\n' << str << '\n' #define DEMO(...) std::cout << #__VA_ARGS__ << " = " << __VA_ARGS__ << '\n' int main() { COUT("Creating shared container"); std::shared_ptr<Container> cont = std::make_shared<Container>(); DEMO(cont.use_count()); DEMO(cont->memberObj.use_count()); COUT("Creating member"); cont->CreateMember(); DEMO(cont.use_count()); DEMO(cont->memberObj.use_count()); COUT("Creating another shared container"); std::shared_ptr<Container> cont2 = cont; DEMO(cont.use_count()); DEMO(cont->memberObj.use_count()); DEMO(cont2.use_count()); DEMO(cont2->memberObj.use_count()); COUT("GetAsMyObj"); std::shared_ptr<MyObj> myobj1 = cont->GetAsMyObj(); DEMO(myobj1.use_count()); DEMO(cont.use_count()); DEMO(cont->memberObj.use_count()); DEMO(cont2.use_count()); DEMO(cont2->memberObj.use_count()); COUT("Copying alias obj"); std::shared_ptr<MyObj> myobj2 = myobj1; DEMO(myobj1.use_count()); DEMO(myobj2.use_count()); DEMO(cont.use_count()); DEMO(cont->memberObj.use_count()); DEMO(cont2.use_count()); DEMO(cont2->memberObj.use_count()); COUT("Resetting cont2"); cont2.reset(); DEMO(myobj1.use_count()); DEMO(myobj2.use_count()); DEMO(cont.use_count()); DEMO(cont->memberObj.use_count()); COUT("Resetting myobj2"); myobj2.reset(); DEMO(myobj1.use_count()); DEMO(cont.use_count()); DEMO(cont->memberObj.use_count()); COUT("Resetting cont"); cont.reset(); DEMO(myobj1.use_count()); DEMO(cont.use_count()); }
输出
Creating shared container cont.use_count() = 1 cont->memberObj.use_count() = 0 Creating member MyObj constructed cont.use_count() = 1 cont->memberObj.use_count() = 1 Creating another shared container cont.use_count() = 2 cont->memberObj.use_count() = 1 cont2.use_count() = 2 cont2->memberObj.use_count() = 1 GetAsMyObj myobj1.use_count() = 3 cont.use_count() = 3 cont->memberObj.use_count() = 1 cont2.use_count() = 3 cont2->memberObj.use_count() = 1 Copying alias obj myobj1.use_count() = 4 myobj2.use_count() = 4 cont.use_count() = 4 cont->memberObj.use_count() = 1 cont2.use_count() = 4 cont2->memberObj.use_count() = 1 Resetting cont2 myobj1.use_count() = 3 myobj2.use_count() = 3 cont.use_count() = 3 cont->memberObj.use_count() = 1 Resetting myobj2 myobj1.use_count() = 2 cont.use_count() = 2 cont->memberObj.use_count() = 1 Resetting cont myobj1.use_count() = 1 cont.use_count() = 0 MyObj destructed
[编辑] 参见
(C++11) |
具有唯一对象所有权语义的智能指针 (类模板) |
(C++11) |
对由 std::shared_ptr 管理的对象的弱引用 (类模板) |