std::shared_ptr

来自cppreference.com
< cpp‎ | memory
 
 
内存管理库
(仅用于阐述*)
分配器
未初始化内存算法
受约束的未初始化内存算法
内存资源
未初始化存储(C++20 前)
(C++17 弃用)
(C++17 弃用)
垃圾收集器支持(C++23 前)
(C++11)(C++23 前)
(C++11)(C++23 前)
(C++11)(C++23 前)
(C++11)(C++23 前)
(C++11)(C++23 前)
(C++11)(C++23 前)
 
 
在标头 <memory> 定义
template<class T >class shared_ptr;
(C++11 起)

std::shared_ptr 是一种通过指针保持对象共享所有权的智能指针。多个 shared_ptr 对象可持有同一对象。下列情况之一出现时销毁对象并解分配其内存:

  • 最后剩下的持有对象的 shared_ptr 被销毁;
  • 最后剩下的持有对象的 shared_ptr 被通过 operator=reset() 赋值为另一指针。

delete 表达式或在构造期间提供给 shared_ptr 的定制删除器销毁对象。

shared_ptr 能在存储指向一个对象的指针时共享另一对象的所有权。此特性能用于在持有其所属对象时,指向成员对象。存储的指针可以使用 get()、解引用或比较运算符访问。被管理指针在使用计数抵达零时传递给删除器。

shared_ptr 也可不持有对象,该情况下称它为空 (empty)(若以别名使用构造函数创建,空 shared_ptr 可拥有非空的存储指针)。

shared_ptr 的所有特化都满足可复制构造(CopyConstructible) 可复制赋值(CopyAssignable) 可小于比较(LessThanComparable) 的要求,并可按语境转换bool

多个线程能在不同的 shared_ptr 对象上调用所有成员函数(包含复制构造函数与复制赋值)而不附加同步,即使这些实例是同一对象的副本且共享所有权也是如此。若多个执行线程访问同一 shared_ptr 对象而不同步,且任一线程使用 shared_ptr 的非 const 成员函数,则将出现数据竞争;std::atomic<shared_ptr> 能用于避免数据竞争。

目录

[编辑]成员类型

成员类型 定义
element_type
T(C++17 前)
std::remove_extent_t<T>(C++17 起)
weak_type(C++17 起)std::weak_ptr<T>

[编辑]成员函数

构造新的 shared_ptr
(公开成员函数)[编辑]
如果没有更多 shared_ptr 指向所持有的对象,则析构该对象
(公开成员函数)[编辑]
shared_ptr 赋值
(公开成员函数)[编辑]
修改器
替换所管理的对象
(公开成员函数)[编辑]
交换所管理的对象
(公开成员函数)[编辑]
观察器
返回存储的指针
(公开成员函数)[编辑]
解引用存储的指针
(公开成员函数)[编辑]
(C++17)
提供到所存储数组的索引访问
(公开成员函数)[编辑]
返回 shared_ptr 所指对象的引用计数
(公开成员函数)[编辑]
(C++20 前)
检查所管理对象是否仅由当前 shared_ptr 的对象管理
(公开成员函数)[编辑]
检查是否有关联的管理对象
(公开成员函数)[编辑]
提供基于持有者的共享指针排序
(公开成员函数)[编辑]
(C++26)
提供基于所有者的共享指针散列
(公开成员函数)[编辑]
提供基于所有者的共享指针相等比较
(公开成员函数)[编辑]

[编辑]非成员函数

创建管理一个新对象的共享指针
(函数模板)[编辑]
创建管理一个用分配器分配的新对象的共享指针
(函数模板)[编辑]
应用 static_castdynamic_castconst_castreinterpret_cast 到被存储指针
(函数模板)[编辑]
返回指定类型中的删除器,若其拥有
(函数模板)[编辑]
(C++20 移除)(C++20 移除)(C++20 移除)(C++20 移除)(C++20 移除)(C++20)
与另一个 shared_ptrnullptr 进行比较
(函数模板)[编辑]
输出存储的指针的值到输出流
(函数模板)[编辑]
特化 std::swap 算法
(函数模板)[编辑]
std::shared_ptr 特化的原子操作
(函数模板)[编辑]

[编辑]辅助类

原子共享指针
(类模板特化)[编辑]
std::shared_ptr 的散列支持
(类模板特化)[编辑]

[编辑]推导指引(C++17 起)

[编辑]注解

只能通过复制构造或复制赋值其值给另一 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)();}

[编辑]实现说明

在典型的实现中,std::shared_ptr 只保有两个指针:

  • 所存储的指针(get()) 所返回的指针)
  • 指向控制块 的指针

控制块是一个动态分配的对象,其中包含:

  • 指向被管理对象的指针或被管理对象本身
  • 删除器(类型擦除)
  • 分配器(类型擦除)
  • 持有被管理对象的 shared_ptr 的数量
  • 涉及被管理对象的 weak_ptr 的数量

以调用 std::make_sharedstd::allocate_shared 创建 shared_ptr 时,以单次分配创建控制块和被管理对象。被管理对象在控制块的数据成员中原位构造。通过 shared_ptr 的构造函数之一创建 shared_ptr 时,被管理对象和控制块只能分离分配。此情况中,控制块存储指向被管理对象的指针。

shared_ptr 直接持有的指针即是 get() 所返回的指针;而控制块所持有的指针/对象则是引用计数归零时会被删除的那个。两者并不一定相等。

shared_ptr 的析构函数会将控制块中的共享所有者数量减一,如果该计数器减至零,控制块就会调用被管理对象的析构函数。但控制块本身直到 std::weak_ptr 计数器同样归零时会解分配其自身。

既存实现中,若有共享指针指向同一控制块,则会增加弱指针计数 ([1], [2])。

为满足线程安全要求,引用计数器典型地用等价于以 std::memory_order_relaxed 调用 std::atomic::fetch_add 进行自增(自减则要求更强的定序,以安全销毁控制块)。

[编辑]示例

#include <chrono>#include <iostream>#include <memory>#include <mutex>#include <thread>   usingnamespace std::chrono_literals;   struct Base { Base(){std::cout<<"Base::Base()\n";}   // 注意:此处非虚析构函数 OK ~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;// 线程安全,虽然自增共享的 use_count{staticstd::mutex io_mutex;std::lock_guard<std::mutex> lk(io_mutex); print("线程中的局部指针:", lp);}}   int main(){ std::shared_ptr<Base> p =std::make_shared<Derived>();   print("创建共享的 Derived (为 Base 指针)", p);   std::thread t1{thr, p}, t2{thr, p}, t3{thr, p}; p.reset();// 从 main 释放所有权   print("在 3 个线程间共享所有权并从 main 释放所有权:", p);   t1.join(); t2.join(); t3.join();   std::cout<<"线程全部已完成,最后一个删除了 Derived。\n";}

可能的输出:

Base::Base() Derived::Derived() 创建共享的 Derived (为 Base 指针) get() = 0x118ac30, use_count() = 1 在 3 个线程间共享所有权并从 main 释放所有权: get() = 0, use_count() = 0 线程中的局部指针: get() = 0x118ac30, use_count() = 5 线程中的局部指针: get() = 0x118ac30, use_count() = 4 线程中的局部指针: get() = 0x118ac30, use_count() = 2 Derived::~Derived() Base::~Base() 已完成,最后一个删除了 Derived。

[编辑]示例

#include <iostream>#include <memory>   struct MyObj { MyObj(){std::cout<<"构造 MyObj\n";}   ~MyObj(){std::cout<<"析构 MyObj\n";}};   struct Container :std::enable_shared_from_this<Container>// 注: 公开继承{ std::shared_ptr<MyObj> memberObj;   void CreateMember(){ memberObj =std::make_shared<MyObj>();}   std::shared_ptr<MyObj> GetAsMyObj(){// 为成员使用使用别名 shared_ptrreturn 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("创建共享容器"); std::shared_ptr<Container> cont =std::make_shared<Container>(); DEMO(cont.use_count()); DEMO(cont->memberObj.use_count());   COUT("创建成员"); cont->CreateMember(); DEMO(cont.use_count()); DEMO(cont->memberObj.use_count());   COUT("创建另一个共享容器"); 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("复制别名对象"); 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("重置 cont2"); cont2.reset(); DEMO(myobj1.use_count()); DEMO(myobj2.use_count()); DEMO(cont.use_count()); DEMO(cont->memberObj.use_count());   COUT("重置 myobj2"); myobj2.reset(); DEMO(myobj1.use_count()); DEMO(cont.use_count()); DEMO(cont->memberObj.use_count());   COUT("重置 cont"); cont.reset(); DEMO(myobj1.use_count()); DEMO(cont.use_count());}

输出:

创建共享容器 cont.use_count() = 1 cont->memberObj.use_count() = 0   创建成员 构造 MyObj cont.use_count() = 1 cont->memberObj.use_count() = 1   创建另一个共享容器 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   复制别名对象 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   重置 cont2 myobj1.use_count() = 3 myobj2.use_count() = 3 cont.use_count() = 3 cont->memberObj.use_count() = 1   重置 myobj2 myobj1.use_count() = 2 cont.use_count() = 2 cont->memberObj.use_count() = 1   重置 cont myobj1.use_count() = 1 cont.use_count() = 0 析构 MyObj

[编辑]参阅

(C++11)
拥有独有对象所有权语义的智能指针
(类模板)[编辑]
(C++11)
std::shared_ptr 所管理对象的弱引用
(类模板)[编辑]
close