与旨在独占资源的 std::unique_ptr
不同,std::shared_ptr
专门解决多个智能指针共同拥有同一资源的需求。
这意味着可以有任意数量的 std::shared_ptr
指向同一对象。内部通过引用计数(reference count)来跟踪共享该资源的 std::shared_ptr
数量。只要至少还有一个 std::shared_ptr
指向该资源,资源就不会被释放;当最后一个 std::shared_ptr
离开作用域或被重新赋值时,资源才被析构。
与 std::unique_ptr
一样,std::shared_ptr
位于头文件 <memory>
。
示例:共享所有权
#include <iostream>
#include <memory> // for std::shared_ptr
class Resource
{
public:
Resource() { std::cout << "Resource acquired\n"; }
~Resource() { std::cout << "Resource destroyed\n"; }
};
int main()
{
Resource* res{ new Resource };
std::shared_ptr<Resource> ptr1{ res };
{
std::shared_ptr<Resource> ptr2{ ptr1 }; // 由 ptr1 拷贝构造
std::cout << "Killing one shared pointer\n";
} // ptr2 离开作用域,但资源仍在
std::cout << "Killing another shared pointer\n";
return 0;
} // ptr1 离开作用域,最后一个引用消失,资源被销毁
输出:
Resource acquired
Killing one shared pointer
Killing another shared pointer
Resource destroyed
重要区别:从裸指针独立创建多个 shared_ptr 的危险
下面示例看似相似,却会导致崩溃:
#include <iostream>
#include <memory>
class Resource { /* 同上 */ };
int main()
{
Resource* res{ new Resource };
std::shared_ptr<Resource> ptr1{ res };
{
std::shared_ptr<Resource> ptr2{ res }; // 直接从裸指针创建
std::cout << "Killing one shared pointer\n";
} // ptr2 离开作用域,误认为独占资源,提前释放
std::cout << "Killing another shared pointer\n";
return 0;
} // ptr1 再次释放同一资源 → 未定义行为(崩溃)
输出:
Resource acquired
Killing one shared pointer
Resource destroyed
Killing another shared pointer
Resource destroyed
根本原因:两个 std::shared_ptr
分别从同一裸指针独立创建,各自维护独立的控制块,引用计数互不知晓。
最佳实践:若需多个 std::shared_ptr
共享同一资源,必须通过拷贝现有 std::shared_ptr
来创建。
std::make_shared
与 std::make_unique
类似,std::make_shared
(C++11 可用)推荐用于创建 std::shared_ptr
:
#include <iostream>
#include <memory>
class Resource { /* 同上 */ };
int main()
{
auto ptr1{ std::make_shared<Resource>() };
{
auto ptr2{ ptr1 }; // 正确拷贝,共享引用计数
std::cout << "Killing one shared pointer\n";
}
std::cout << "Killing another shared pointer\n";
}
使用 `std::make_shared` 的好处:
- 语法简洁,类型安全;
- 避免独立创建多个 `shared_ptr` 导致的问题;
- **性能优势**:一次性分配资源对象与控制块内存,减少二次分配开销。
---
## 深入实现细节
- `std::unique_ptr` 内部仅需单个指针;
- `std::shared_ptr` 内部保存**两个指针**:
1. 指向托管对象;
2. 指向**控制块**(动态分配,保存引用计数等元数据)。
若通过裸指针构造多个 `shared_ptr`,每个实例都会独立创建控制块 → 重复释放。
通过拷贝/赋值现有 `shared_ptr`,共享同一控制块,引用计数正确递增。
---
## 从 unique_ptr 转换
```cpp
std::unique_ptr<Resource> up{ std::make_unique<Resource>() };
std::shared_ptr<Resource> sp{ std::move(up) }; // 合法,资源转移
反之,**不能**将 `shared_ptr` 转为 `unique_ptr`,因为共享语义违背独占语义。
若函数返回智能指针,建议返回 `std::unique_ptr`,调用方按需转为 `std::shared_ptr` 即可。
---
## 注意事项
- 若任一 `std::shared_ptr` 未被正确销毁(例如所属对象泄漏),资源亦无法释放;
- C++17 及更早版本,`std::shared_ptr` 不支持 C 风格数组;C++20 起支持数组特化。
---
## 结论
`std::shared_ptr` 适用于**多指针共享资源**场景。
资源的生命周期由**最后一个引用**的 `std::shared_ptr` 控制,确保在适当时机自动、安全地释放。