std::shared_ptr
| 在标头 <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
|
| ||||
weak_type (C++17 起)
|
std::weak_ptr<T>
|
成员函数
构造新的 shared_ptr (公开成员函数) | |
如果没有更多 shared_ptr 指向所持有的对象,则析构该对象 (公开成员函数) | |
对 shared_ptr 赋值 (公开成员函数) | |
修改器 | |
| 替换所管理的对象 (公开成员函数) | |
| 交换所管理的对象 (公开成员函数) | |
观察器 | |
| 返回存储的指针 (公开成员函数) | |
| 解引用存储的指针 (公开成员函数) | |
(C++17) |
提供到所存储数组的索引访问 (公开成员函数) |
返回 shared_ptr 所指对象的引用计数 (公开成员函数) | |
(C++20 前) |
检查所管理对象是否仅由当前 shared_ptr 的对象管理 (公开成员函数) |
| 检查是否有关联的管理对象 (公开成员函数) | |
| 提供基于持有者的共享指针排序 (公开成员函数) | |
(C++26) |
提供基于所有者的共享指针散列 (公开成员函数) |
(C++26) |
提供基于所有者的共享指针相等比较 (公开成员函数) |
非成员函数
| 创建管理一个新对象的共享指针 (函数模板) | |
| 创建管理一个用分配器分配的新对象的共享指针 (函数模板) | |
| 应用 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 特化的原子操作 (函数模板) |
辅助类
(C++20) |
原子共享指针 (类模板特化) |
(C++11) |
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_shared 或 std::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>
using namespace 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
{
static std::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_ptr
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("创建共享容器");
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 所管理对象的弱引用 (类模板) |